Esempio n. 1
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is EnumFragment enumFragment))
            {
                return(false);
            }

            if (enumFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            using (writer.WriteIndentedBlock(prefix: $"enum {TS.Type(fragment.Name)} "))
            {
                foreach (var item in enumFragment.Items)
                {
                    writer.WriteIndentedLine($"{TS.Field(item.Name)} = {item.Value},");
                }
            }

            var itemHasDisplayNameHint = enumFragment.Items.Any(i => i.Hints.ContainsKey("display-name"));

            using (writer.WriteIndentedBlock(prefix: $"const names = ", suffix: ";"))
            {
                foreach (var item in enumFragment.Items)
                {
                    writer.WriteIndentedLine($"[{TS.Type(enumFragment.Name)}.{TS.Field(item.Name)}]: {TS.String(TS.Field(item.Name))},");
                }
            }

            if (itemHasDisplayNameHint)
            {
                using (writer.WriteIndentedBlock(prefix: $"const displayNames = ", suffix: ";"))
                {
                    foreach (var item in enumFragment.Items)
                    {
                        writer.WriteIndentedLine($"[{TS.Type(enumFragment.Name)}.{TS.Field(item.Name)}]: {TS.String(item.Hints["display-name"] ?? "")},");
                    }
                }
            }

            using (writer.WriteIndentedBlock(prefix: $"namespace {TS.Type(enumFragment.Name)} "))
            {
                if (enumFragment.Hints.TryGetValue("enum-flags", out var enumFlags) && string.Equals(enumFlags, "true", StringComparison.OrdinalIgnoreCase))
                {
                    writer.WriteIndentedLine($@"export function create(value: any): {TS.Type(enumFragment.Name)} {{
    if (typeof value !== 'number') {{
        throw new Error(`Value '${{value}}' is not valid for enum {TS.Type(enumFragment.Name)}`);
    }}

    let remainder = value;
    for (let k in {TS.Type(enumFragment.Name)}) {{
        const v = {TS.Type(enumFragment.Name)}[k];
        if (!{TS.Type(enumFragment.Name)}.hasOwnProperty(v)) {{
            continue;
        }}

        remainder = remainder & ~v;
    }}

	if (remainder !== 0) {{
		throw new Error(`Remainder '${{remainder}}' of '${{value}}' is not valid for enum {TS.Type(enumFragment.Name)}`);
	}}

	return value as {TS.Type(enumFragment.Name)};
}};");
                }
                else
                {
                    writer.WriteIndentedLine($@"export function create(value: any): {TS.Type(enumFragment.Name)} {{
	if (!{TS.Type(enumFragment.Name)}.hasOwnProperty(value)) {{
		throw new Error(`Value '${{value}}' is not valid for enum {TS.Type(enumFragment.Name)}`);
	}}

	return value as {TS.Type(enumFragment.Name)};
}}");
                }
                writer.WriteSeparatingLine();

                using (writer.WriteIndentedBlock(prefix: $"export function getValues(): {TS.Type(enumFragment.Name)}[] "))
                {
                    using (writer.WriteIndentedBlock(prefix: "return ", open: "[", close: "]", suffix: ";"))
                    {
                        foreach (var item in enumFragment.Items)
                        {
                            writer.WriteIndentedLine($"{TS.Type(enumFragment.Name)}.{TS.Field(item.Name)},");
                        }
                    }
                }

                using (writer.WriteIndentedBlock(prefix: $"export function getNames(): string[] "))
                {
                    using (writer.WriteIndentedBlock(prefix: "return ", open: "[", close: "]", suffix: ";"))
                    {
                        foreach (var item in enumFragment.Items)
                        {
                            writer.WriteIndentedLine($"{TS.String(TS.Field(item.Name))},");
                        }
                    }
                }

                writer.WriteIndentedLine($@"export function getName(value: {TS.Type(enumFragment.Name)}): string {{
	const name = names[value];

	if (name === undefined) {{
		throw new Error(`Cannot get name of {TS.Type(enumFragment.Name)} '${{value}}'`);
	}}

	return name;
}}");
                writer.WriteSeparatingLine();

                if (itemHasDisplayNameHint)
                {
                    writer.WriteIndentedLine($@"export function getDisplayName(value: {TS.Type(enumFragment.Name)}): string {{
	const displayName = displayNames[value];

	if (displayName === undefined) {{
		throw new Error(`Cannot get display name of {TS.Type(enumFragment.Name)} '${{value}}'`);
	}}

	return displayName;
}}");
                    writer.WriteSeparatingLine();
                }
            }

            context.Export(enumFragment.Name, @default: true);

            return(true);
        }
Esempio n. 2
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is ObjectFragment objectFragment))
            {
                return(false);
            }

            if (objectFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            var genericSuffix = string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name));

            using (writer.WriteIndentedBlock(prefix: $"class {objectFragment.Name}{(genericSuffix.Length > 0 ? $"<{genericSuffix}>" : "")} "))
            {
                foreach (var field in objectFragment.Fields)
                {
                    context.Import("mobx", "observable");

                    writer.WriteIndentedLine("@observable.ref");
                    writer.WriteIndentedLine($"{TS.Field(field.Name)}: {context.Resolve(field.Type)};");
                    writer.WriteSeparatingLine();
                }

                if (objectFragment.GenericArguments.Any())
                {
                    var genericParameters = $"<{string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name))}>";
                    var factoryParameters = string.Join(", ", objectFragment.GenericArguments.Select(a => $"{a.Name}_factory: {{ create(source: any): {a.Name} }}"));

                    using (writer.WriteIndentedBlock(prefix: $"static create{genericParameters}({factoryParameters}): {{ create: (source: any) => {objectFragment.Name}{genericParameters} }} "))
                    {
                        using (writer.WriteIndentedBlock(prefix: $"return ", suffix: ";"))
                        {
                            using (writer.WriteIndentedBlock(prefix: $"create: (source: any) => "))
                            {
                                writer.WriteIndentedLine($"const result = new {objectFragment.Name}{genericParameters}();");
                                foreach (var field in objectFragment.Fields)
                                {
                                    writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                                }
                                writer.WriteIndentedLine("return result;");
                            }
                        }
                    }
                }
                else
                {
                    using (writer.WriteIndentedBlock(prefix: $"static create(source: any): {objectFragment.Name} "))
                    {
                        writer.WriteIndentedLine($"const result = new {objectFragment.Name}();");
                        foreach (var field in objectFragment.Fields)
                        {
                            writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                        }
                        writer.WriteIndentedLine("return result;");
                    }
                }
            }

            context.Export(objectFragment.Name, @default: true);

            return(true);
        }
Esempio n. 3
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is ObjectFragment objectFragment))
            {
                return(false);
            }

            if (objectFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            var genericSuffix = string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name));

            using (writer.WriteIndentedBlock(prefix: $"class {TS.Type(objectFragment.Name)}{(genericSuffix.Length > 0 ? $"<{genericSuffix}>" : "")} "))
            {
                if (objectFragment.Constants.Any())
                {
                    foreach (var constant in objectFragment.Constants)
                    {
                        writer.WriteIndentedLine($"static readonly {TS.Field(constant.Name)}: {context.Resolve(constant.Type)} = {TS.Constant(constant.Value)};");
                    }
                    writer.WriteSeparatingLine();
                }

                foreach (var field in objectFragment.Fields)
                {
                    context.Import("mobx", "observable");

                    writer.WriteIndentedLine("@observable.ref");
                    writer.WriteIndentedLine($"{TS.Field(field.Name)}: {context.Resolve(field.Type)} = {context.ResolveDefaultValue(field.Type)};");
                    writer.WriteSeparatingLine();
                }

                if (objectFragment.GenericArguments.Any())
                {
                    var genericParameters = $"<{string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name))}>";
                    var factoryParameters = string.Join(", ", objectFragment.GenericArguments.Select(a => $"{a.Name}_factory: {{ create(source: any): {a.Name} }}"));

                    using (writer.WriteIndentedBlock(prefix: $"static create{genericParameters}({factoryParameters}): {{ create: (source: any) => {TS.Type(objectFragment.Name)}{genericParameters} }} "))
                    {
                        using (writer.WriteIndentedBlock(prefix: $"return ", suffix: ";"))
                        {
                            using (writer.WriteIndentedBlock(prefix: $"create: (source: any) => "))
                            {
                                writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}{genericParameters}();");
                                foreach (var field in objectFragment.Fields)
                                {
                                    writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                                }
                                writer.WriteIndentedLine("return result;");
                            }
                        }
                    }
                }
                else
                {
                    using (writer.WriteIndentedBlock(prefix: $"static create(source: any): {TS.Type(objectFragment.Name)} "))
                    {
                        writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}();");
                        foreach (var field in objectFragment.Fields)
                        {
                            writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                        }
                        writer.WriteIndentedLine("return result;");
                    }
                }

                var hasValidation = objectFragment.Fields.Any(f => f.Hints.Any(h => h.Key.StartsWith("validation:")));
                if (hasValidation)
                {
                    using (writer.WriteIndentedBlock(prefix: $"static validation = ", suffix: ";"))
                    {
                        foreach (var field in objectFragment.Fields)
                        {
                            field.Hints.TryGetValue("validation:is-required", out var isRequired);
                            field.Hints.TryGetValue("validation:min-length", out var minLength);
                            field.Hints.TryGetValue("validation:max-length", out var maxLength);

                            if (string.IsNullOrEmpty(isRequired) && string.IsNullOrEmpty(minLength) && string.IsNullOrEmpty(maxLength))
                            {
                                continue;
                            }

                            using (writer.WriteIndentedBlock(prefix: $"{TS.Field(field.Name)}: ", suffix: ","))
                            {
                                if (isRequired == "true")
                                {
                                    writer.WriteIndentedLine("required: true,");
                                }
                                if ((!string.IsNullOrEmpty(minLength) && minLength != "-1") || (!string.IsNullOrEmpty(maxLength) && maxLength != "-1"))
                                {
                                    using (writer.WriteIndentedBlock(prefix: "length: ", suffix: ","))
                                    {
                                        if (!string.IsNullOrEmpty(minLength) && minLength != "-1")
                                        {
                                            writer.WriteIndentedLine($"min: {minLength},");
                                        }
                                        if (!string.IsNullOrEmpty(maxLength) && maxLength != "-1")
                                        {
                                            writer.WriteIndentedLine($"max: {maxLength},");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            context.Export(objectFragment.Name, @default: true);

            return(true);
        }
Esempio n. 4
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is ServiceFragment serviceFragment))
            {
                return(false);
            }

            if (serviceFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            using (writer.WriteIndentedBlock(prefix: $"class {TS.Type(serviceFragment.Name)} "))
            {
                if (serviceFragment.Constants.Any())
                {
                    foreach (var constant in serviceFragment.Constants)
                    {
                        writer.WriteIndentedLine($"static readonly {TS.Field(constant.Name)}: {context.Resolve(constant.Type)} = {TS.Constant(constant.Value)};");
                    }
                    writer.WriteSeparatingLine();
                }

                context.Import(Options.RpcClientModule, Options.RpcClientImport);
                using (writer.WriteIndentedBlock(prefix: $"constructor(client: {Options.RpcClientImport}) "))
                {
                    writer.WriteIndentedLine("this.client = client;");
                }
                writer.WriteSeparatingLine();

                writer.WriteIndentedLine($"private client: {Options.RpcClientImport};");
                writer.WriteSeparatingLine();

                foreach (var method in serviceFragment.Methods)
                {
                    var rpcMethodName = method.Hints["jsonrpc-name"] ?? method.Name;

                    var parameters = method.Parameters
                                     .Select(p => $"{TS.Parameter(p.Name)}: {context.Resolve(p.Type)}")
                                     .ToList();

                    using (writer.WriteIndentedBlock(prefix: $"async {TS.Method(method.Name)}Async({string.Join(", ", parameters)}): Promise<{context.Resolve(method.ReturnType)}> "))
                    {
                        if (method.ReturnType.Name == "void")
                        {
                            writer.WriteIndentedLine($"await this.client.callAsync('{rpcMethodName}', {{ {string.Join(", ", method.Parameters.Select(p => p.Name))} }});");
                        }
                        else
                        {
                            writer.WriteIndentedLine($"const result = await this.client.callAsync('{rpcMethodName}', {{ {string.Join(", ", method.Parameters.Select(p => p.Name))} }});");
                            writer.WriteIndentedLine($"return {context.CreateExpression(method.ReturnType, "result")};");
                        }
                    }
                    writer.WriteSeparatingLine();
                }
            }
            writer.WriteSeparatingLine();

            context.Export(serviceFragment.Name, @default: true);

            return(true);
        }
Esempio n. 5
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is ObjectFragment objectFragment))
            {
                return(false);
            }

            if (objectFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            var genericSuffix = string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name));

            using (writer.WriteIndentedBlock(prefix: $"class {TS.Type(objectFragment.Name)}{(genericSuffix.Length > 0 ? $"<{genericSuffix}>" : "")} "))
            {
                if (objectFragment.Constants.Any())
                {
                    foreach (var constant in objectFragment.Constants)
                    {
                        writer.WriteIndentedLine($"static readonly {TS.Field(constant.Name)}: {context.Resolve(constant.Type)} = {TS.Constant(constant.Value)};");
                    }
                    writer.WriteSeparatingLine();
                }

                if (objectFragment.Fields.Count > 0)
                {
                    foreach (var field in objectFragment.Fields)
                    {
                        context.Import("mobx", "observable");

                        // this seem to be sometimes wrong? todo: investigate
                        //if (field.Type.Kind == TypeKind.Array)
                        if (field.Type.Module == null && field.Type.Name == "array")
                        {
                            writer.WriteIndentedLine("@observable.shallow");
                        }
                        else
                        {
                            writer.WriteIndentedLine("@observable.ref");
                        }
                        writer.WriteIndentedLine($"{TS.Field(field.Name)}: {context.Resolve(field.Type)} = {context.ResolveDefaultValue(field.Type)};");
                        writer.WriteSeparatingLine();
                    }

                    if (objectFragment.GenericArguments.Any())
                    {
                        var genericParameters = $"<{string.Join(", ", objectFragment.GenericArguments.Select(a => a.Name))}>";
                        var factoryParameters = string.Join(", ", objectFragment.GenericArguments.Select(a => $"{a.Name}_factory: {{ create(source: any): {a.Name} }}"));

                        using (writer.WriteIndentedBlock(prefix: $"static create{genericParameters}({factoryParameters}): {{ create: (source: any) => {TS.Type(objectFragment.Name)}{genericParameters} }} "))
                        {
                            using (writer.WriteIndentedBlock(prefix: $"return ", suffix: ";"))
                            {
                                using (writer.WriteIndentedBlock(prefix: $"create: (source: any) => "))
                                {
                                    writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}{genericParameters}();");
                                    foreach (var field in objectFragment.Fields)
                                    {
                                        writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                                    }
                                    writer.WriteIndentedLine("return result;");
                                }
                            }
                        }
                        writer.WriteSeparatingLine();
                        using (writer.WriteIndentedBlock(prefix: $"static copy{genericParameters}(source: {TS.Type(objectFragment.Name)}{genericParameters}, destination: {TS.Type(objectFragment.Name)}{genericParameters}): void "))
                        {
                            foreach (var field in objectFragment.Fields)
                            {
                                writer.WriteIndentedLine($"destination.{TS.Field(field.Name)} = source.{TS.Field(field.Name)};");
                            }
                        }
                        writer.WriteSeparatingLine();
                        using (writer.WriteIndentedBlock(prefix: $"static clone{genericParameters}(source: {TS.Type(objectFragment.Name)}{genericParameters}): {TS.Type(objectFragment.Name)}{genericParameters} "))
                        {
                            writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}{genericParameters}();");
                            writer.WriteIndentedLine($"{TS.Type(objectFragment.Name)}.copy{genericParameters}(source, result);");
                            writer.WriteIndentedLine("return result;");
                        }
                    }
                    else
                    {
                        using (writer.WriteIndentedBlock(prefix: $"static create(source: any): {TS.Type(objectFragment.Name)} "))
                        {
                            writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}();");
                            foreach (var field in objectFragment.Fields)
                            {
                                writer.WriteIndentedLine($"result.{TS.Field(field.Name)} = {context.CreateExpression(field.Type, $"source.{TS.Field(field.Name)}")};");
                            }
                            writer.WriteIndentedLine("return result;");
                        }
                        writer.WriteSeparatingLine();
                        using (writer.WriteIndentedBlock(prefix: $"static copy(source: {TS.Type(objectFragment.Name)}, destination: {TS.Type(objectFragment.Name)}): void "))
                        {
                            foreach (var field in objectFragment.Fields)
                            {
                                writer.WriteIndentedLine($"destination.{TS.Field(field.Name)} = source.{TS.Field(field.Name)};");
                            }
                        }
                        writer.WriteSeparatingLine();
                        using (writer.WriteIndentedBlock(prefix: $"static clone(source: {TS.Type(objectFragment.Name)}): {TS.Type(objectFragment.Name)} "))
                        {
                            writer.WriteIndentedLine($"const result = new {TS.Type(objectFragment.Name)}();");
                            writer.WriteIndentedLine($"{TS.Type(objectFragment.Name)}.copy(source, result);");
                            writer.WriteIndentedLine("return result;");
                        }
                    }
                }
            }

            context.Export(objectFragment.Name, @default: true);

            return(true);
        }
Esempio n. 6
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is EnumFragment enumFragment))
            {
                return(false);
            }

            if (enumFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            using (writer.WriteIndentedBlock(prefix: $"enum {fragment.Name} "))
            {
                foreach (var item in enumFragment.Items)
                {
                    writer.WriteIndentedLine($"{TS.Field(item.Name)} = {item.Value},");
                }
            }

            var itemHasDisplayNameHint = enumFragment.Items.Any(i => i.Hints.ContainsKey("display-name"));

            using (writer.WriteIndentedBlock(prefix: $"const names = ", suffix: ";"))
            {
                foreach (var item in enumFragment.Items)
                {
                    writer.WriteIndentedLine($"[{enumFragment.Name}.{TS.Field(item.Name)}]: '{TS.Field(item.Name)}',");
                }
            }

            if (itemHasDisplayNameHint)
            {
                using (writer.WriteIndentedBlock(prefix: $"const displayNames = ", suffix: ";"))
                {
                    foreach (var item in enumFragment.Items)
                    {
                        writer.WriteIndentedLine($"[{enumFragment.Name}.{TS.Field(item.Name)}]: '{item.Hints["display-name"] ?? ""}',");
                    }
                }
            }

            using (writer.WriteIndentedBlock(prefix: $"namespace {enumFragment.Name} "))
            {
                writer.WriteIndentedLine($@"export function create(value: any): {enumFragment.Name} {{
	if (!{enumFragment.Name}.hasOwnProperty(value)) {{
		throw new Error(`Value '${{value}}' is not valid for enum {enumFragment.Name}`);
	}}

	return value as {enumFragment.Name};
}}");
                writer.WriteSeparatingLine();

                using (writer.WriteIndentedBlock(prefix: $"export function getValues(): {enumFragment.Name}[] "))
                {
                    using (writer.WriteIndentedBlock(prefix: "return ", open: "[", close: "]", suffix: ";"))
                    {
                        foreach (var item in enumFragment.Items)
                        {
                            writer.WriteIndentedLine($"{enumFragment.Name}.{TS.Field(item.Name)},");
                        }
                    }
                }

                using (writer.WriteIndentedBlock(prefix: $"export function getNames(): string[] "))
                {
                    using (writer.WriteIndentedBlock(prefix: "return ", open: "[", close: "]", suffix: ";"))
                    {
                        foreach (var item in enumFragment.Items)
                        {
                            writer.WriteIndentedLine($"'{TS.Field(item.Name)}',");
                        }
                    }
                }

                writer.WriteIndentedLine($@"export function getName(value: {enumFragment.Name}): string {{
	const name = names[value];

	if (name === undefined) {{
		throw new Error(`Cannot get name of {enumFragment.Name} '${{value}}'`);
	}}

	return name;
}}");
                writer.WriteSeparatingLine();

                if (itemHasDisplayNameHint)
                {
                    writer.WriteIndentedLine($@"export function getDisplayName(value: {enumFragment.Name}): string {{
	const displayName = displayNames[value];

	if (displayName === undefined) {{
		throw new Error(`Cannot get display name of {enumFragment.Name} '${{value}}'`);
	}}

	return displayName;
}}");
                    writer.WriteSeparatingLine();
                }
            }

            context.Export(enumFragment.Name, @default: true);

            return(true);
        }
Esempio n. 7
0
        public bool Render(TypeScriptModuleContext context, Fragment fragment, IndentedTextWriter writer)
        {
            if (!(fragment is ServiceFragment serviceFragment))
            {
                return(false);
            }

            if (serviceFragment.Hints.TryGetValue("source-type", out var sourceType))
            {
                writer.WriteIndentedLine($"// source: {sourceType}");
                writer.WriteSeparatingLine();
            }

            var hasConstants = serviceFragment.Constants.Any();
            var hasMethods   = serviceFragment.Methods.Any();

            var classDeclaration = $"class {TS.Type(serviceFragment.Name)} ";

            if (hasMethods)
            {
                context.Import("@stackino/due", "Tag");
                writer.WriteIndentedLine($"const {TS.Type(serviceFragment.Name)}Tag = new Tag<{TS.Type(serviceFragment.Name)}>('{context.PackageContext.Package.Name} {TS.Type(serviceFragment.Name)}');");
                writer.WriteSeparatingLine();

                context.Export($"{TS.Type(serviceFragment.Name)}Tag");

                context.Import("@stackino/due", "Injectable");
                classDeclaration += "extends Injectable ";
            }
            using (writer.WriteIndentedBlock(prefix: classDeclaration))
            {
                if (hasConstants)
                {
                    foreach (var constant in serviceFragment.Constants)
                    {
                        writer.WriteIndentedLine($"static readonly {TS.Field(constant.Name)}: {context.Resolve(constant.Type)} = {TS.Constant(constant.Value)};");
                    }
                    writer.WriteSeparatingLine();
                }

                if (hasMethods)
                {
                    context.Import("@stackino/due-plugin-odachirpcclient", "RpcClientTag");
                    writer.WriteIndentedLine("private readonly client = this.$dependency(RpcClientTag);");
                    writer.WriteSeparatingLine();

                    foreach (var method in serviceFragment.Methods)
                    {
                        var rpcMethodName = method.Hints["jsonrpc-name"] ?? method.Name;

                        var parameters = method.Parameters
                                         .Select(p => $"{TS.Parameter(p.Name)}: {context.Resolve(p.Type)}")
                                         .ToList();

                        using (writer.WriteIndentedBlock(prefix: $"async {TS.Method(method.Name)}Async({string.Join(", ", parameters)}): Promise<{context.Resolve(method.ReturnType)}> "))
                        {
                            if (method.ReturnType.Name == "void")
                            {
                                writer.WriteIndentedLine($"await this.client.callAsync({TS.String(rpcMethodName)}, {{ {string.Join(", ", method.Parameters.Select(p => p.Name))} }});");
                            }
                            else
                            {
                                writer.WriteIndentedLine($"const result = await this.client.callAsync({TS.String(rpcMethodName)}, {{ {string.Join(", ", method.Parameters.Select(p => p.Name))} }});");
                                writer.WriteIndentedLine($"return {context.CreateExpression(method.ReturnType, "result")};");
                            }
                        }
                        writer.WriteSeparatingLine();
                    }
                }
            }
            writer.WriteSeparatingLine();

            context.Export(serviceFragment.Name, @default: true);

            return(true);
        }