UNPKG

typhonjs-escomplex-commons

Version:
1,211 lines (899 loc) 27 kB
/* eslint-disable eqeqeq */ import ASTUtil from './ASTUtil'; import expressionPrecedence from './expressionPrecedence'; let ArrayExpression, BinaryExpression, ForInStatement, FunctionDeclaration, Property, RestElement; export default { Program(node, state) { const { lineEnd, output } = state; const indent = state.indent.repeat(state.indentLevel); const statements = node.body; for (let i = 0; i < statements.length; i++) { const statement = statements[i]; output.write(indent); this[statement.type](statement, state); output.write(lineEnd); } }, BlockStatement(node, state) { const { lineEnd, output } = state; const indent = state.indent.repeat(state.indentLevel++); const statementIndent = indent + state.indent; output.write('{'); const statements = node.body; if (statements != null && statements.length > 0) { output.write(lineEnd); for (let i = 0; i < statements.length; i++) { const statement = statements[i]; output.write(statementIndent); this[statement.type](statement, state); output.write(lineEnd); } output.write(indent); } output.write('}'); state.indentLevel--; }, EmptyStatement(node, state) { state.output.write(';'); }, ExpressionStatement(node, state) { const { output } = state; const precedence = expressionPrecedence[node.expression.type]; if (precedence === 17 || (precedence === 3 && node.expression.left.type[0] === 'O')) { // Should always have parentheses or is an AssignmentExpression to an ObjectPattern output.write('('); this[node.expression.type](node.expression, state); output.write(')'); } else { this[node.expression.type](node.expression, state); } output.write(';'); }, IfStatement(node, state) { const { output } = state; output.write('if ('); output.operators.push('if'); this[node.test.type](node.test, state); output.write(') '); this[node.consequent.type](node.consequent, state); if (node.alternate != null) { output.write(' else '); output.operators.push('else'); this[node.alternate.type](node.alternate, state); } }, LabeledStatement(node, state) { this[node.label.type](node.label, state); state.output.write(': '); this[node.body.type](node.body, state); }, BreakStatement(node, state) { const { output } = state; output.write('break'); output.operators.push('break'); if (node.label) { output.write(' '); this[node.label.type](node.label, state); } output.write(';'); }, ContinueStatement(node, state) { const { output } = state; output.write('continue'); output.operators.push('continue'); if (node.label) { output.write(' '); this[node.label.type](node.label, state); } output.write(';'); }, WithStatement(node, state) { const { output } = state; output.write('with ('); output.operators.push('with'); this[node.object.type](node.object, state); output.write(') '); this[node.body.type](node.body, state); }, SwitchStatement(node, state) { const { lineEnd, output } = state; const indent = state.indent.repeat(state.indentLevel++); state.indentLevel++; const caseIndent = indent + state.indent; const statementIndent = caseIndent + state.indent; output.write('switch ('); output.operators.push('switch'); this[node.discriminant.type](node.discriminant, state); output.write(`) \{${lineEnd}`); const { cases: occurences } = node; const { length: occurencesCount } = occurences; for (let i = 0; i < occurencesCount; i++) { const occurence = occurences[i]; if (occurence.test) { output.write(`${caseIndent}case `); output.operators.push('case'); this[occurence.test.type](occurence.test, state); output.write(`:${lineEnd}`); } else { output.write(`${caseIndent}default:${lineEnd}`); output.operators.push('default'); } const { consequent } = occurence; const { length: consequentCount } = consequent; for (let j = 0; j < consequentCount; j++) { const statement = consequent[j]; output.write(statementIndent); this[statement.type](statement, state); output.write(lineEnd); } } state.indentLevel -= 2; output.write(`${indent}}`); }, ReturnStatement(node, state) { const { output } = state; output.write('return'); output.operators.push('return'); if (node.argument) { output.write(' '); this[node.argument.type](node.argument, state); } output.write(';'); }, ThrowStatement(node, state) { const { output } = state; output.write('throw '); output.operators.push('throw'); this[node.argument.type](node.argument, state); output.write(';'); }, TryStatement(node, state) { const { output } = state; output.write('try '); output.operators.push('try'); this[node.block.type](node.block, state); if (node.handler) { const { handler } = node; output.write(' catch ('); output.operators.push('catch'); this[handler.param.type](handler.param, state); output.write(') '); this[handler.body.type](handler.body, state); } if (node.finalizer) { output.write(' finally '); output.operators.push('finally'); this[node.finalizer.type](node.finalizer, state); } }, WhileStatement(node, state) { const { output } = state; output.operators.push('while'); output.write('while ('); this[node.test.type](node.test, state); output.write(') '); this[node.body.type](node.body, state); }, DoWhileStatement(node, state) { const { output } = state; output.operators.push('dowhile'); output.write('do '); this[node.body.type](node.body, state); output.write(' while ('); this[node.test.type](node.test, state); output.write(');'); }, ForStatement(node, state) { const { output } = state; output.write('for ('); output.operators.push('for'); if (node.init != null) { state.noTrailingSemicolon = true; this[node.init.type](node.init, state); state.noTrailingSemicolon = false; } output.write('; '); if (node.test) { this[node.test.type](node.test, state); } output.write('; '); if (node.update) { this[node.update.type](node.update, state); } output.write(') '); this[node.body.type](node.body, state); }, ForInStatement: ForInStatement = function(node, state) { const { output } = state; output.write('for ('); const { left } = node, { type } = left; state.noTrailingSemicolon = true; this[type](left, state); state.noTrailingSemicolon = false; // Identifying whether node.type is `ForInStatement` or `ForOfStatement` output.write(node.type[3] === 'I' ? ' in ' : ' of '); output.operators.push(node.type[3] === 'I' ? 'forin' : 'forof'); this[node.right.type](node.right, state); output.write(') '); this[node.body.type](node.body, state); }, ForOfStatement: ForInStatement, DebuggerStatement(node, state) { state.output.write(`debugger;${state.lineEnd}`); }, FunctionDeclaration: FunctionDeclaration = function(node, state) { const { output } = state; output.write(node.generator ? 'function* ' : 'function '); output.operators.push(node.generator ? 'function*' : 'function'); if (node.id) { output.write(node.id.name); output.operands.push(node.id.name); } ASTUtil.formatSequence(node.params, state, this); output.write(' '); this[node.body.type](node.body, state); }, FunctionExpression: FunctionDeclaration, VariableDeclaration(node, state) { const { output } = state; const { declarations } = node; output.write(`${node.kind} `); output.operators.push(node.kind); const { length } = declarations; if (length > 0) { this.VariableDeclarator(declarations[0], state); for (let i = 1; i < length; i++) { output.write(', '); this.VariableDeclarator(declarations[i], state); } } if (state.noTrailingSemicolon !== true) { output.write(';'); } }, VariableDeclarator(node, state) { const { output } = state; this[node.id.type](node.id, state); if (node.init != null) { output.write(' = '); output.operators.push('='); this[node.init.type](node.init, state); } }, ClassDeclaration(node, state) { const { output } = state; output.write('class '); output.operators.push('class'); if (node.id) { output.write(`${node.id.name} `); } if (node.superClass) { output.write('extends '); output.operators.push('extends'); this[node.superClass.type](node.superClass, state); output.write(' '); } this.BlockStatement(node.body, state); }, ImportDeclaration(node, state) { const { output } = state; output.write('import '); output.operators.push('import'); const { specifiers } = node; const { length } = specifiers; if (length > 0) { let i = 0, specifier; while (i < length) { if (i > 0) { output.write(', '); } specifier = specifiers[i]; const type = specifier.type[6]; if (type === 'D') { output.write(specifier.local.name); // ImportDefaultSpecifier i++; } else if (type === 'N') { output.write(`* as ${specifier.local.name}`); // ImportNamespaceSpecifier i++; } else { break; // ImportSpecifier } } if (i < length) { output.write('{'); for (; ;) { specifier = specifiers[i]; const { name } = specifier.imported; output.write(name); if (name !== specifier.local.name) { output.write(` as ${specifier.local.name}`); } if (++i < length) { output.write(', '); } else { break; } } output.write('}'); } output.write(' from '); output.operators.push('from'); } this.Literal(node.source, state); output.write(';'); }, ExportDefaultDeclaration(node, state) { const { output } = state; output.write('export default '); output.operators.push('export'); output.operators.push('default'); this[node.declaration.type](node.declaration, state); // All expression nodes except `FunctionExpression` if (expressionPrecedence[node.declaration.type] && node.declaration.type[0] !== 'F') { output.write(';'); } }, ExportNamedDeclaration(node, state) { const { output } = state; output.write('export '); output.operators.push('export'); if (node.declaration) { this[node.declaration.type](node.declaration, state); } else { output.write('{'); output.operators.push('{}'); const { specifiers } = node, { length } = specifiers; if (length > 0) { for (let i = 0; ;) { const specifier = specifiers[i]; const { name } = specifier.local; output.write(name); if (name !== specifier.exported.name) { output.write(` as ${specifier.exported.name}`); } if (++i < length) { output.write(', '); } else { break; } } } output.write('}'); if (node.source) { output.write(' from '); this.Literal(node.source, state); } output.write(';'); } }, ExportAllDeclaration(node, state) { const { output } = state; output.write('export * from '); output.operators.push('export'); output.operators.push('*'); this.Literal(node.source, state); output.write(';'); }, MethodDefinition(node, state) { const { output } = state; if (node.static) { output.write('static '); output.operators.push('static'); } switch (node.kind[0]) { case 'g': // `get` case 's': // `set` output.write(`${node.kind} `); output.operators.push(node.kind); break; } if (node.value.generator) { output.write('*'); } if (node.computed) { output.write('['); this[node.key.type](node.key, state); output.write(']'); } else { this[node.key.type](node.key, state); } ASTUtil.formatSequence(node.value.params, state, this); output.write(' '); this[node.value.body.type](node.value.body, state); }, ClassExpression(node, state) { this.ClassDeclaration(node, state); }, ArrowFunctionExpression(node, state) { const { output } = state; const { params } = node; if (params != null) { // If params[0].type[0] starts with 'I', it can't be `ImportDeclaration` nor `IfStatement` and thus is // `Identifier` if (params.length === 1 && params[0].type[0] === 'I') { output.write(params[0].name); } else { ASTUtil.formatSequence(node.params, state, this); } } output.write(' => '); output.operators.push('function=>'); if (node.body.type[0] === 'O') { output.write('('); this.ObjectExpression(node.body, state); output.write(')'); } else { this[node.body.type](node.body, state); } }, ThisExpression(node, state) { state.output.write('this'); state.output.operators.push('this'); }, Super(node, state) { state.output.write('super'); state.output.operators.push('super'); }, RestElement: RestElement = function(node, state) { state.output.write('...'); state.output.operators.push('... (rest)'); this[node.argument.type](node.argument, state); }, SpreadElement(node, state) { state.output.write('...'); state.output.operators.push('... (spread)'); this[node.argument.type](node.argument, state); }, YieldExpression(node, state) { const { output } = state; output.write(node.delegate ? 'yield*' : 'yield'); output.operators.push(node.delegate ? 'yield*' : 'yield'); if (node.argument) { output.write(' '); this[node.argument.type](node.argument, state); } }, TemplateLiteral(node, state) { const { output } = state; const { quasis, expressions } = node; const { length } = expressions; output.write('`'); for (let i = 0; i < length; i++) { const expression = expressions[i]; output.write(quasis[i].value.raw); output.write('${'); this[expression.type](expression, state); output.write('}'); } output.write(quasis[quasis.length - 1].value.raw); output.write('`'); }, TaggedTemplateExpression(node, state) { this[node.tag.type](node.tag, state); this[node.quasi.type](node.quasi, state); }, ArrayExpression: ArrayExpression = function(node, state) { const { output } = state; output.operators.push('[]'); output.write('['); if (node.elements.length > 0) { const { elements } = node, { length } = elements; for (let i = 0; ;) { const element = elements[i]; if (element != null) { this[element.type](element, state); } if (++i < length) { output.write(', '); output.operators.push(','); } else { if (element == null) { output.write(', '); output.operators.push(','); } break; } } } output.write(']'); }, ArrayPattern: ArrayExpression, ObjectExpression(node, state) { const { lineEnd, output } = state; const indent = state.indent.repeat(state.indentLevel++); const propertyIndent = indent + state.indent; output.operators.push('{}'); output.write('{'); if (node.properties.length > 0) { output.write(lineEnd); const comma = `,${lineEnd}`, { properties } = node, { length } = properties; for (let i = 0; ;) { const property = properties[i]; output.write(propertyIndent); this.Property(property, state); if (++i < length) { output.write(comma); } else { break; } } output.write(lineEnd); output.write(`${indent}}`); } else { output.write('}'); } state.indentLevel--; }, Property: Property = function(node, state) { if (node.method || (node.kind && node.kind[0] !== 'i')) { this.MethodDefinition(node, state); // Either a method or of kind `set` or `get` (not `init`) } else { const { output } = state; if (!node.shorthand) { if (node.computed) { output.operators.push('[]'); output.write('['); this[node.key.type](node.key, state); output.write(']'); } else { this[node.key.type](node.key, state); } output.operators.push(':'); output.write(': '); } this[node.value.type](node.value, state); } }, ObjectPattern(node, state) { const { output } = state; output.operators.push('{}'); output.write('{'); if (node.properties.length > 0) { const { properties } = node, { length } = properties; for (let i = 0; ;) { this[properties[i].type](properties[i], state); if (++i < length) { output.write(', '); } else { break; } } } output.write('}'); }, SequenceExpression(node, state) { ASTUtil.formatSequence(node.expressions, state, this); }, UnaryExpression(node, state) { const { output } = state; output.operators.push(`${node.operator} (${node.prefix ? 'pre' : 'post'}fix)`); if (node.prefix) { output.write(node.operator); if (node.operator.length > 1) { output.write(' '); } if (expressionPrecedence[node.argument.type] < expressionPrecedence.UnaryExpression) { output.write('('); this[node.argument.type](node.argument, state); output.write(')'); } else { this[node.argument.type](node.argument, state); } } else { // FIXME: This case never occurs this[node.argument.type](node.argument, state); state.output.write(node.operator); } }, UpdateExpression(node, state) { // Always applied to identifiers or members, no parenthesis check needed state.output.operators.push(`${node.operator} (${node.prefix ? 'pre' : 'post'}fix)`); if (node.prefix) { state.output.write(node.operator); this[node.argument.type](node.argument, state); } else { this[node.argument.type](node.argument, state); state.output.write(node.operator); } }, AssignmentExpression(node, state) { this[node.left.type](node.left, state); state.output.write(` ${node.operator} `); this[node.right.type](node.right, state); state.output.operators.push(node.operator); }, AssignmentPattern(node, state) { this[node.left.type](node.left, state); state.output.write(' = '); this[node.right.type](node.right, state); state.output.operators.push('='); }, BinaryExpression: BinaryExpression = function(node, state) { const { output } = state; output.operators.push(node.operator); if (node.operator === 'in') { // Avoids confusion in `for` loops initializers output.write('('); ASTUtil.formatBinaryExpressionPart(node.left, node, false, state, this); output.write(` ${node.operator} `); ASTUtil.formatBinaryExpressionPart(node.right, node, true, state, this); output.write(')'); } else { ASTUtil.formatBinaryExpressionPart(node.left, node, false, state, this); output.write(` ${node.operator} `); ASTUtil.formatBinaryExpressionPart(node.right, node, true, state, this); } }, LogicalExpression: BinaryExpression, ConditionalExpression(node, state) { const { output } = state; if (expressionPrecedence[node.test.type] > expressionPrecedence.ConditionalExpression) { this[node.test.type](node.test, state); } else { output.operators.push('()'); output.write('('); this[node.test.type](node.test, state); output.write(')'); } output.write(' ? '); this[node.consequent.type](node.consequent, state); output.write(' : '); this[node.alternate.type](node.alternate, state); output.operators.push(':?'); }, NewExpression(node, state) { const { output } = state; output.write('new '); output.operators.push('new'); if (expressionPrecedence[node.callee.type] < expressionPrecedence.CallExpression || ASTUtil.hasCallExpression(node.callee)) { output.write('('); this[node.callee.type](node.callee, state); output.write(')'); output.operators.push('()'); } else { this[node.callee.type](node.callee, state); } ASTUtil.formatSequence(node['arguments'], state, this); }, CallExpression(node, state) { const { output } = state; if (expressionPrecedence[node.callee.type] < expressionPrecedence.CallExpression) { output.write('('); this[node.callee.type](node.callee, state); output.write(')'); output.operators.push('()'); } else { this[node.callee.type](node.callee, state); } ASTUtil.formatSequence(node['arguments'], state, this); }, MemberExpression(node, state) { const { output } = state; if (expressionPrecedence[node.object.type] < expressionPrecedence.MemberExpression) { output.write('('); this[node.object.type](node.object, state); output.write(')'); output.operators.push('()'); } else { this[node.object.type](node.object, state); } if (node.computed) { output.write('['); this[node.property.type](node.property, state); output.write(']'); output.operators.push('[]'); } else { output.write('.'); output.operators.push('.'); this[node.property.type](node.property, state); } }, MetaProperty(node, state) { state.output.write(`${node.meta.name}.${node.property.name}`); state.output.operators.push('.'); state.output.operands.push(node.meta.name); state.output.operands.push(node.property.name); }, Identifier(node, state) { state.output.write(node.name); state.output.operands.push(node.name); }, Literal(node, state) { if (node.raw != null) { state.output.write(node.raw); state.output.operands.push(node.raw); } else if (node.regex != null) { this.RegExpLiteral(node, state); } else { state.output.write(JSON.stringify(node.value)); } }, RegExpLiteral(node, state) { const { regex } = node; state.output.write(`new RegExp(${JSON.stringify(regex.pattern)}, ${JSON.stringify(regex.flags)})`); }, // Babylon AST nodes --------------------------------------------------------------------------------------------- ObjectProperty: Property, RestProperty: RestElement, BooleanLiteral(node, state) { state.output.write(node.value); state.output.operands.push(JSON.stringify(node.value)); }, DirectiveLiteral(node, state) { state.output.write(node.value); state.output.operands.push(JSON.stringify(node.value)); }, NullLiteral(node, state) { state.output.write('null'); state.output.operands.push('null'); }, NumericLiteral(node, state) { state.output.write(node.value); state.output.operands.push(JSON.stringify(node.value)); }, StringLiteral(node, state) { if (node.extra != null && node.extra.raw != null) { state.output.write(node.extra.raw); state.output.operands.push(node.extra.raw); } else { state.output.write(JSON.stringify(node.value)); state.output.operands.push(JSON.stringify(node.value)); } } };