def map_class_def(babel_node, node, parents): class_name = build.identifier(node.name) bases = babel_node['bases'] if not bases: super_class = None else: if len(bases) == 1: super_class = bases[0] else: super_class = build.call_expression( # TODO: Use constant callee=build.identifier('__multi__'), args=bases, ensure_native_compatibility=False, ) body = babel_node['body'] # Change functions to methods and variable declarations to class properties # that were created by the recursion already. class_expression = { 'type': 'ClassExpression', 'id': class_name, 'superClass': super_class, 'body': { 'type': 'ClassBody', 'body': body, }, } if babel_node['keywords']: print('keywords was provided for class definition. ignored...') return { 'type': 'VariableDeclaration', 'declarations': [ { 'type': 'VariableDeclarator', 'id': class_name, 'init': utils.apply_decorators( babel_node['decorator_list'], build.call_expression( # TODO: Use constant callee=build.identifier('__def__'), args=[class_expression], ensure_native_compatibility=False, ) ), } ], 'kind': 'var', }
def map_subscript(babel_node, node, parents): if isinstance(node.slice, ast.ExtSlice): raise ValueError('Advanced slicing (ExtSlice) is not supported.') args = [babel_node['value']] slice_data = babel_node['slice'] if isinstance(node.slice, ast.Index): args += [ build.string('index'), slice_data['value'], ] # isinstance(node.slice, ast.Slice) else: args += [ build.string('slice'), slice_data['lower'], slice_data['upper'], slice_data['step'], ] return build.call_expression( callee=build.identifier(JsHelperNames.SUBSCRIPT), args=args, ensure_native_compatibility=False, )
def _map_function_def(babel_node, node, parents): common_parts = utils.function_definition_parts(babel_node, node) func_name = common_parts['__name__'] params = common_parts['params'] body = common_parts['body'] function_definition = build.call_expression( # TODO: Use constant callee=build.identifier('__def__'), args=[{ 'type': 'FunctionExpression', 'id': func_name, 'params': params, 'body': body, }], ensure_native_compatibility=False, ) return { 'type': 'VariableDeclaration', 'declarations': [{ 'type': 'VariableDeclarator', 'id': func_name, 'init': utils.apply_decorators(babel_node['decorator_list'], function_definition), }], 'kind': 'var', }
def map_dict(babel_node, node, parents): return build.call_expression( callee=build.identifier(JsHelperNames.DICT_CONSTRUCTOR), args=[ build.array_expression([ (build.array_expression([key, value]) if key is not None else build.spread( build.call_expression( callee=build.member_expression( object=value, property=build.identifier('entries'), ), ensure_native_compatibility=False, ))) for key, value in zip(babel_node['keys'], babel_node['values']) ]) ], )
def map_tuple(babel_node, node, parents): mapped_list = map_list(babel_node, node, parents) if isinstance(node.ctx, ast.Load): return build.call_expression( callee=build.identifier(JsHelperNames.TUPLE_CONSTRUCTOR), args=[mapped_list], ensure_native_compatibility=False, ) else: return mapped_list
def _map_function_def_to_class_method(babel_node, node, parents): common_parts = utils.function_definition_parts(babel_node, node) method_name = common_parts['__name__'] raw_method_name = method_name['name'] params = common_parts['params'] body = common_parts['body'] decorators = [] use_static_keyword = False inject_self = True for decorator in babel_node['decorator_list']: # '@classmethod' does not take any arguments # => it must be an identifier. decorator_is_identifier = check.is_identifier(decorator) decorator_requires_static_keyword = ( decorator_is_identifier and decorator['name'] in ['classmethod', 'staticmethod']) if decorator_requires_static_keyword: use_static_keyword = True if decorator['name'] == 'staticmethod': inject_self = False else: if decorator_is_identifier: expression = decorator else: # Use the kwargs supporting way to call the function to avoid # actual expression as decorator which is technically possible. # NOTE: This corresponds to # 'build.call_expression(ensure_native_compatibility=False)' # in 'utils.apply_decorators'. expression = utils.unpack_value( decorator, 'expression_statement.conditional_expression__consequent') decorators.append({'type': 'Decorator', 'expression': expression}) # Remove 'self' as 1st positional argument # and remove 'self = __use_kwarg__(_, self, "self");' # and instead inject 'var self = this' into the method body. del params[0]['elements'][0] inner_body = body['body'] if inner_body: del inner_body[0] if inject_self: inner_body.insert( 0, build.variable_declaration( left=build.identifier('self'), right=build.this(), )) return { 'type': 'ClassMethod', 'kind': ('constructor' if raw_method_name == 'constructor' else 'method'), 'decorators': decorators, 'static': use_static_keyword, 'key': method_name, 'params': params, 'body': body, 'id': None, 'generator': False, 'expression': False, 'async': False, }
def map_arg(babel_node, node, parents): return build.identifier(node.arg)
def function_definition_parts(babel_node, node): '''Returns parts of the function babel AST node that are common for normal function definitions and class/instance methods.''' arguments = babel_node['args'] args = arguments.get('args', []) defaults = arguments.get('defaults', []) vararg = arguments.get('vararg', None) kwonlyargs = arguments.get('kwonlyargs', []) kw_defaults = arguments.get('kw_defaults', []) kwarg = arguments.get('kwarg', None) diff = len(args) - len(defaults) if diff > 0: defaults = [NO_DEFAULT_FOR_ARG for i in range(0, diff)] + defaults # Prepend custom JS code the function body (for creating the kwargs behavior). # body = babel_node['body'] # if len(args) > 0: args_identifier = '__arguments__' if True: # var [[a, b=1, ...c], {d, e=3, ...f}] = __get_args__(args) body = ([ build.variable_declaration( left=build.array_destructuring( bare_pattern=True, props=[ build.array_destructuring( props=[ (arg if defaults[i] is NO_DEFAULT_FOR_ARG else (arg, defaults[i])) for i, arg in enumerate(args) ], rest=vararg, bare_pattern=True, ), build.object_destructuring( props=[(kwonlyarg if kw_defaults[i] is None else (kwonlyarg, kw_defaults[i])) for i, kwonlyarg in enumerate(kwonlyargs)], rest=kwarg, bare_pattern=True, ), ]), right=build.call_expression( callee=build.identifier(JsHelperNames.GET_ARGS_AND_KWARGS), args=[build.identifier(args_identifier)], ensure_native_compatibility=False, ), ), ] + [ build.assignment( left=arg, right=build.call_expression( callee=build.identifier( JsHelperNames.CONSUME_KWARG_IF_POSSIBLE), args=[kwarg, arg, build.string(arg['name'])], ensure_native_compatibility=False, ), ) for arg in args if kwarg is not None ] + babel_node['body']) return { '__name__': build.identifier(node.name), 'params': [ build.rest(build.identifier(args_identifier)) # build.array_destructuring( # props=[ # ( # arg # if defaults[i] is NO_DEFAULT_FOR_ARG # else (arg, defaults[i]) # ) # for i, arg in enumerate(args) # ], # rest=vararg, # bare_pattern=True, # ), # build.object_destructuring( # props=[ # ( # kwonlyarg # if kw_defaults[i] is None # else (kwonlyarg, kw_defaults[i]) # ) # for i, kwonlyarg in enumerate(kwonlyargs) # ], # rest=kwarg, # bare_pattern=True, # ), ], 'body': { 'type': 'BlockStatement', 'body': body, 'directives': [], }, }
def map_set(babel_node, node, parents): return { 'type': 'NewExpression', 'callee': build.identifier('Set'), 'arguments': babel_node['elts'], }
def map_name(babel_node, node, parents): # id = node.id # one of ast.Load, ast.Store, ast.Delete # ctx = node.ctx return build.identifier(node.id)
def map_attribute(babel_node, node, parents): return build.member_expression( object=babel_node['value'], property=build.identifier(node.attr), )
def map_keyword(babel_node, node, parents): return { 'arg': None if node.arg is None else build.identifier(node.arg), 'value': babel_node['value'], }