decaffeinate-parser
Version:
A better AST for CoffeeScript, inspired by CoffeeScriptRedux.
80 lines (79 loc) • 4.02 kB
JavaScript
import { SourceType } from 'coffee-lex';
import { Assign, Obj, Value } from 'decaffeinate-coffeescript2/lib/coffeescript/nodes';
import { inspect } from 'util';
import { AssignOp, Block, BoundAsyncFunction, BoundFunction, BoundGeneratorFunction, ClassProtoAssignOp, Constructor, Identifier, MemberAccessOp, This } from '../nodes';
import getLocation from '../util/getLocation';
import isCommentOnlyNode from '../util/isCommentOnlyNode';
import mapAny from './mapAny';
export default function mapBlock(context, node) {
var childContext = context;
if (context.parseState.isInClassBody()) {
// Replicate a bug in CoffeeScript: at any block where we see an
// object-style proto assignment, stop considering proto assignments in any
// sub-traversals. This is taken from the walkBody implementation.
var hasProtoAssignChild = node.expressions.some(function (child) { return child instanceof Value && child.isObject(true); });
if (hasProtoAssignChild) {
childContext = childContext.updateState(function (s) { return s.dropCurrentClass(); });
}
}
var _a = getLocation(context, node), line = _a.line, column = _a.column, start = _a.start, end = _a.end, raw = _a.raw;
var previousTokenIndex = context.sourceTokens
.indexOfTokenNearSourceIndex(start)
.previous();
var previousToken = previousTokenIndex
? context.sourceTokens.tokenAtIndex(previousTokenIndex)
: null;
var inline = previousToken
? previousToken.type !== SourceType.NEWLINE
: false;
return new Block(line, column, start, end, raw, node.expressions
.filter(function (expression) { return !isCommentOnlyNode(expression); })
.map(function (expression) { return mapChild(context, childContext, expression); })
.reduce(function (arr, current) { return arr.concat(current); }, []), inline);
}
function mapChild(blockContext, childContext, node) {
if (blockContext.parseState.isInClassBody() &&
node instanceof Value &&
node.isObject(true)) {
var obj = node.base;
if (!(obj instanceof Obj)) {
throw new Error('Expected isObject node to be an object.');
}
var statements = [];
for (var _i = 0, _a = obj.properties; _i < _a.length; _i++) {
var property = _a[_i];
if (isCommentOnlyNode(property)) {
continue;
}
if (property instanceof Assign) {
var _b = getLocation(childContext, property), line = _b.line, column = _b.column, start = _b.start, end = _b.end, raw = _b.raw;
var key = mapAny(childContext, property.variable);
var value = mapAny(childContext, property.value);
var Node_1 = ClassProtoAssignOp;
if (key instanceof Identifier && key.data === 'constructor') {
Node_1 = Constructor;
}
else if (key instanceof MemberAccessOp &&
key.expression instanceof This) {
Node_1 = AssignOp;
}
var assignment = new Node_1(line, column, start, end, raw, key, value);
statements.push(assignment);
if (assignment instanceof ClassProtoAssignOp &&
(assignment.expression instanceof BoundFunction ||
assignment.expression instanceof BoundGeneratorFunction ||
assignment.expression instanceof BoundAsyncFunction)) {
blockContext.parseState.recordBoundMethod(assignment);
}
if (assignment instanceof Constructor) {
blockContext.parseState.recordConstructor(assignment);
}
}
else {
throw new Error("unexpected class assignment: " + inspect(property));
}
}
return statements;
}
return [mapAny(childContext, node)];
}