예제 #1
0
def get_single_type_annotation(definitions: DefinitionType,
                               property_: PropertyType) -> ast.AST:
    # If no property definition, use any
    if property_ is None:
        return ast.AnyTypeAnnotation()

    # Parse type
    type_name = property_.get("type")
    ref_key = property_.get("$ref")

    if type_name is None and ref_key is None:
        return ast.AnyTypeAnnotation()
    elif type_name in ("integer", "number"):
        return ast.NumberTypeAnnotation()
    elif type_name == "string":
        return ast.StringTypeAnnotation()
    elif type_name == "boolean":
        return ast.BooleanTypeAnnotation()
    elif type_name == "object":
        return get_object_type_annotation(definitions, property_)
    elif type_name == "array":
        items_definition = property_.get("items")
        parameter = get_single_type_annotation(definitions, items_definition)

        return ast.GenericTypeAnnotation(
            ast.Identifier("Array"),
            type_parameters=ast.TypeParameterInstantiation([parameter]))
    elif ref_key is not None:
        definition = definitions[ref_key]

        return ast.GenericTypeAnnotation(ast.Identifier(definition["title"]))

    # Nothing matches
    raise NotImplementedError("{}: {}".format(definitions, property_))
예제 #2
0
    def klass_constructor(self, properties: PropertiesType, requireds: RequiredType):
        # Build constructor body
        body = []

        for key in sorted(properties.keys()):
            property_ = properties[key]
            required = key in requireds

            # Left assignment
            assign_left = ast.MemberExpression(ast.ThisExpression(), ast.Identifier(key))

            # Right assignment
            assign_right = self.get_member_right_assignment(key, property_, required)

            # Add property assignment
            klass_property = ast.ExpressionStatement(
                ast.AssignmentExpression(assign_left, assign_right)
            )

            body.append(klass_property)

        # Build constructor parameters
        param_type = ast.TypeAnnotation(ast.GenericTypeAnnotation(id_=ast.Identifier("Object")))
        param = ast.AssignmentPattern(
            left=ast.Identifier("data", type_annotation=param_type), right=ast.ObjectExpression()
        )

        params = [param]

        # Return constructor method
        block = ast.BlockStatement(body=body)

        return ast.ClassMethod(
            key=ast.Identifier("constructor"), kind="constructor", params=params, body=block
        )
예제 #3
0
    def klass(self, definition: DefinitionType) -> ast.ExportNamedDeclaration:
        # Build class property Flow definition
        body = []
        required = definition.get("required", [])
        properties = definition.get("properties", {})

        for key in sorted(properties.keys()):
            # Add property type definition
            property_ = properties[key]
            is_required = key in required
            has_default = "default" in property_

            property_annotation = get_type_annotation(
                self.definitions, property_, required=(is_required or has_default)
            )
            property_def = ast.ClassProperty(
                key=ast.Identifier(key), typeAnnotation=ast.TypeAnnotation(property_annotation)
            )

            body.append(property_def)

        # Add class constructor
        if len(properties):
            body.append(self.klass_constructor(properties, required))

        # Return class definition
        return ast.ExportNamedDeclaration(
            declaration=ast.ClassDeclaration(
                id_=ast.Identifier(definition["title"]), body=ast.ClassBody(body=body)
            )
        )
예제 #4
0
    def klass(self, definition):
        # Build class property Flow definition
        klass_annotations = []
        required = definition.get("required", ())
        properties = definition.get("properties", {})

        for key in sorted(properties.keys()):
            # Add property type definition
            property_ = properties[key]
            is_required = key in required
            has_default = "default" in property_

            property_annotation = get_type_annotation(
                self.definitions,
                property_,
                required=(is_required or has_default))
            property_def = ast.ObjectTypeProperty(key=ast.Identifier(key),
                                                  value=property_annotation,
                                                  force_variance=True)
            klass_annotations.append(property_def)

        # Add class constructor
        if len(properties):
            klass_annotations.append(self.klass_constructor())

        # Return class definition
        klass = ast.DeclareTypeAlias(
            id_=ast.Identifier(definition["title"]),
            right=ast.ObjectTypeAnnotation(klass_annotations),
        )

        return klass
예제 #5
0
    def _get_default_for_string(self, name, definition):
        test = ast.BinaryExpression(
            left=ast.UnaryExpression(
                operator="typeof",
                argument=ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name)),
            ),
            right=ast.StringLiteral("string"),
        )

        consequent = ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name))
        alternate = ast.StringLiteral(definition["default"])

        return ast.ConditionalExpression(test, consequent, alternate)
예제 #6
0
 def klass_constructor(self):
     return ast.ObjectTypeProperty(
         key=ast.Identifier("constructor"),
         value=ast.FunctionTypeAnnotation(
             params=[
                 ast.FunctionTypeParam(
                     ast.Identifier("data"),
                     type_annotation=ast.NullableTypeAnnotation(
                         ast.GenericTypeAnnotation(
                             ast.Identifier("Object"))),
                 )
             ],
             return_type=ast.VoidTypeAnnotation(),
         ),
         method=True,
     )
예제 #7
0
    def _get_default_for_array(self, name, definition):
        # Test expression
        test = ast.CallExpression(
            callee=ast.MemberExpression(ast.Identifier("Array"), ast.Identifier("isArray")),
            arguments=[ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name))],
        )

        # Consequent expression
        items = definition.get("items", [])

        consequent = ast.MemberExpression(ast.Identifier("data"), property_=ast.Identifier(name))

        if isinstance(items, dict) and "oneOf" in items:
            one_of = items["oneOf"][0]

            if "$ref" not in one_of:
                raise NotImplementedError(
                    "Only 'oneOf' with '$ref's are supported: {}".format(one_of)
                )

            ref_key = one_of["$ref"]
            ref = self.definitions[ref_key]

            if not self.definition_is_primitive_alias(ref):
                consequent = ast.CallExpression(
                    callee=ast.MemberExpression(consequent, ast.Identifier("map")),
                    arguments=[
                        ast.ArrowFunctionExpression(
                            params=[ast.Identifier("v")],
                            body=ast.CallExpression(
                                ast.Identifier(ref["title"]), [ast.Identifier("v")]
                            ),
                        )
                    ],
                )
        elif isinstance(items, list) and len(items):
            raise NotImplementedError("Tuples not implemented yet: {}".format(items))

        # Alternate expression
        alternate = ast.ArrayExpression(
            elements=[ast.NumericLiteral(v) for v in definition["default"]]
        )

        # Return condition
        return ast.ConditionalExpression(test, consequent, alternate)
예제 #8
0
    def type_alias(self, definition):
        aliased_type = get_type_annotation(self.definitions,
                                           definition,
                                           required=True)
        type_alias = ast.DeclareTypeAlias(id_=ast.Identifier(
            definition["title"]),
                                          right=aliased_type)

        return type_alias
예제 #9
0
        def ast_from_dict(d):
            properties = []

            for k, v in d.items():
                key = ast.Identifier(k)
                value = ast.NumericLiteral(v)

                properties.append(ast.ObjectProperty(key, value))

            return ast.ObjectExpression(properties)
예제 #10
0
    def get_member_right_assignment(
        self, key: str, property_: PropertyType, required: bool = False
    ):
        additional_properties = property_.get("additionalProperties")

        if additional_properties is not None:
            if "$ref" not in additional_properties:
                raise NotImplementedError(
                    "Scalar types for additionalProperties not supported yet"
                )

            return self._get_reducer_for_property(key, additional_properties)

        if "default" in property_:
            node = self._get_default_for_property(key, property_)
        elif property_["type"] == "object":
            node = self.get_member_as_object(key, property_, required=required)
        else:
            node = ast.MemberExpression(ast.Identifier("data"), ast.Identifier(key))

        return node
예제 #11
0
    def _get_default_for_object(self, name, definition):
        def ast_from_dict(d):
            properties = []

            for k, v in d.items():
                key = ast.Identifier(k)
                value = ast.NumericLiteral(v)

                properties.append(ast.ObjectProperty(key, value))

            return ast.ObjectExpression(properties)

        test = ast.LogicalExpression(
            left=ast.BinaryExpression(
                left=ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name)),
                right=ast.NullLiteral(),
                operator="!==",
            ),
            right=ast.BinaryExpression(
                left=ast.UnaryExpression(
                    operator="typeof",
                    argument=ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name)),
                ),
                right=ast.StringLiteral("object"),
            ),
        )

        consequent = ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name))
        alternate = ast_from_dict(definition["default"])

        return ast.ConditionalExpression(test, consequent, alternate)
예제 #12
0
def get_object_type_annotation(definitions: DefinitionType,
                               property_: PropertyType) -> ast.AST:
    additionalProperties = property_.get("additionalProperties")

    if additionalProperties:
        annotation = ast.ObjectTypeAnnotation(
            properties=[],
            indexers=[
                ast.ObjectTypeIndexer(
                    id_=ast.Identifier("key"),
                    key=ast.StringTypeAnnotation(),
                    value=get_type_annotation(definitions,
                                              additionalProperties,
                                              required=True),
                )
            ],
        )
    elif "oneOf" in property_:
        annotation = get_oneof_type_annotion(definitions, property_)
    else:
        annotation = ast.GenericTypeAnnotation(ast.Identifier("Object"))

    return annotation
예제 #13
0
    def _get_default_for_integer(self, name, definition):
        test = ast.CallExpression(
            callee=ast.MemberExpression(ast.Identifier("Number"), ast.Identifier("isInteger")),
            arguments=[ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name))],
        )

        consequent = ast.MemberExpression(ast.Identifier("data"), ast.Identifier(name))
        alternate = ast.NumericLiteral(definition["default"])

        return ast.ConditionalExpression(test, consequent, alternate)
예제 #14
0
    def get_member_as_object(self, key: str, property_: PropertyType, required: bool = False):
        if "oneOf" in property_:
            ref_title = self.definitions[property_["oneOf"][0]["$ref"]]["title"]

            if required:
                # new Object(data.key)
                node = ast.NewExpression(
                    callee=ast.Identifier(ref_title),
                    arguments=ast.MemberExpression(
                        object=ast.Identifier(name="data"), property=ast.Identifier(name="x")
                    ),
                )
            else:
                # data.key ? new Object(data.key) : undefined;
                node = ast.ConditionalExpression(
                    test=ast.MemberExpression(
                        object_=ast.Identifier(name="data"), property_=ast.Identifier(name=key)
                    ),
                    consequent=ast.NewExpression(
                        callee=ast.Identifier(name=ref_title),
                        arguments=[
                            ast.MemberExpression(
                                object_=ast.Identifier(name="data"),
                                property_=ast.Identifier(name=key),
                            )
                        ],
                    ),
                    alternate=ast.Identifier(name="undefined"),
                )
        else:
            # data.key
            node = ast.MemberExpression(
                object_=ast.Identifier(name="data"), property_=ast.Identifier(name=key)
            )

        return node
예제 #15
0
    def _get_reducer_for_property(self, key, property_):
        # Object.entries()
        object_entries = ast.CallExpression(
            ast.MemberExpression(ast.Identifier("Object"), ast.Identifier("entries")),
            [ast.MemberExpression(ast.Identifier("data"), ast.Identifier(key))],
        )

        # reduce()
        # ...deconstruct `entry`...
        deconstruct_entry = ast.VariableDeclaration(
            [
                ast.VariableDeclarator(
                    id_=ast.ArrayPattern(
                        [
                            ast.Identifier(
                                "key",
                                type_annotation=ast.TypeAnnotation(ast.StringTypeAnnotation()),
                            ),
                            ast.Identifier(
                                "value",
                                type_annotation=ast.TypeAnnotation(
                                    ast.GenericTypeAnnotation(ast.Identifier("Object"))
                                ),
                            ),
                        ]
                    ),
                    init=ast.TypeCastExpression(
                        ast.Identifier("entry"), ast.TypeAnnotation(ast.AnyTypeAnnotation())
                    ),
                )
            ]
        )

        # ...assign newValue...
        ref_title = self.definitions[property_["$ref"]]["title"]

        new_value = ast.VariableDeclaration(
            [
                ast.VariableDeclarator(
                    ast.Identifier("newValue"),
                    ast.NewExpression(
                        ast.Identifier(ref_title), arguments=[ast.Identifier("value")]
                    ),
                )
            ]
        )

        # ...update acc...
        update_acc = ast.ExpressionStatement(
            ast.AssignmentExpression(
                left=ast.MemberExpression(
                    ast.Identifier("acc"), ast.Identifier("key"), computed=True
                ),
                right=ast.Identifier("newValue"),
            )
        )

        # ...return acc...
        return_acc = ast.ReturnStatement(ast.Identifier("acc"))

        # ...bound together
        reduce = ast.CallExpression(
            callee=ast.MemberExpression(object_entries, ast.Identifier("reduce")),
            arguments=[
                ast.ArrowFunctionExpression(
                    params=[ast.Identifier("acc"), ast.Identifier("entry")],
                    body=ast.BlockStatement(
                        [deconstruct_entry, new_value, update_acc, return_acc]
                    ),
                ),
                ast.ObjectExpression(),
            ],
        )

        # Return reduce
        return reduce