void IDisposable.Dispose() { if (_owner != null && _indent) { _owner._indentLevel--; } _owner = null; }
public Indenter(TypeScriptWriter owner, bool indent) { _owner = owner; _indent = indent; if (indent) { _owner._indentLevel++; } }
private void OutputTypeConversion(TypeScriptWriter writer, string lvalue, ApiTypeDesc type, bool toTypeScript) { var converter = getConverter(type); if (converter == null) { return; } MarkUsedConverters(converter, toTypeScript); writer.WriteLine($"if ({lvalue})"); using (writer.Indent()) writer.WriteLine($"{lvalue} = this.{converter.FunctionName(toTypeScript)}({lvalue});"); }
protected void OutputInterface(TypeScriptWriter writer, ApiInterfaceDesc i) { writer.Write($"interface {getName(i.TsName)}"); if (i.Extends.Any()) { writer.Write(" extends "); writer.Write(i.Extends.Select(e => getNamespace(e.TsName) == getNamespace(i.TsName) ? getName(e.TsName) : e.TsName).Order().JoinString(", ")); } writer.WriteLine(" {"); using (writer.Indent()) { foreach (var prop in i.Properties.OrderBy(p => p.TsName)) { writer.WriteLine($"{prop.TsName}: {prop.TsType.GetTypeScript()};"); } } writer.WriteLine("}"); }
protected void OutputEnum(TypeScriptWriter writer, ApiEnumDesc e) { writer.WriteLine($"type {getName(e.TsName)} = {e.Values.Select(v => v.TsName).Order().JoinString(" | ", "\"", "\"")};"); }
public void Output(TypeScriptWriter writer, ApiDesc api) { writer.WriteLine("// AUTOGENERATED FILE - any manual changes will be lost"); writer.WriteLine(); if (api.Imports.Count > 0) { foreach (var import in api.Imports.Order()) { writer.WriteLine(import); } writer.WriteLine(); } writer.WriteLine("declare global {"); writer.WriteLine(); using (writer.Indent()) { var namespaces = api.Interfaces.Values.Select(i => getNamespace(i.TsName)).Concat(api.Enums.Values.Select(e => getNamespace(e.TsName))).Distinct().Order(); foreach (var ns in new string[] { null }.Concat(namespaces)) { var enums = api.Enums.Values.Where(e => getNamespace(e.TsName) == ns).OrderBy(e => e.TsName).ToList(); var interfaces = api.Interfaces.Values.Where(i => getNamespace(i.TsName) == ns).OrderBy(e => e.TsName).ToList(); if (enums.Count == 0 && interfaces.Count == 0) { continue; } if (ns != null) { writer.WriteLine($"namespace {ns} {{"); writer.WriteLine(); } using (writer.Indent(ns != null)) { foreach (var e in enums) { OutputEnum(writer, e); writer.WriteLine(); } foreach (var i in interfaces) { OutputInterface(writer, i); writer.WriteLine(); } } if (ns != null) { writer.WriteLine("}"); writer.WriteLine(); } } } writer.WriteLine("}"); writer.WriteLine(); writer.WriteLine("export class Services {"); using (writer.Indent()) { foreach (var svc in api.Services.OrderBy(s => s.Name)) { writer.WriteLine($"public readonly {svc.Name}: {svc.Name}Service;"); } writer.WriteLine(); writer.WriteLine("public constructor(hostname: string) {"); using (writer.Indent()) { foreach (var svc in api.Services.OrderBy(s => s.Name)) { writer.WriteLine($"this.{svc.Name} = new {svc.Name}Service(hostname);"); } } writer.WriteLine("}"); } writer.WriteLine("}"); writer.WriteLine(); foreach (var svc in api.Services.OrderBy(s => s.Name)) { OutputService(writer, svc); writer.WriteLine(); } }
protected void OutputService(TypeScriptWriter writer, ApiServiceDesc s) { _convertersUsedFrom = new HashSet <TypeConverter>(); _convertersUsedTo = new HashSet <TypeConverter>(); writer.WriteLine($"export class {s.Name}Service extends ApiServiceBase {{"); writer.WriteLine(); using (writer.Indent()) { writer.WriteLine("private _hostname: string;"); writer.WriteLine(); writer.WriteLine("public constructor(hostname: string) {"); using (writer.Indent()) { writer.WriteLine("super();"); writer.WriteLine("this._hostname = (hostname.substr(-1) == '/') ? hostname : hostname + '/';"); } writer.WriteLine("}"); writer.WriteLine(); foreach (var method in s.Methods.OrderBy(m => m.TsName)) { foreach (var httpMethod in method.HttpMethods.Order()) { bool canDirectReturn = getConverter(method.TsReturnType) == null; writer.Write($"public {(canDirectReturn ? "" : "async ")}{getMethodName(method, httpMethod)}("); bool first = true; foreach (var p in method.Parameters) { if (!first) { writer.Write(", "); } writer.Write($"{p.TsName}: {p.TsType.GetTypeScript()}"); first = false; } writer.WriteLine($"): {string.Format(ReturnTypeTemplate, method.TsReturnType.GetTypeScript())} {{"); using (writer.Indent()) { var url = method.UrlPath; bool first2 = true; foreach (var p in method.Parameters.Where(p => p.Location == ParameterLocation.QueryString).OrderBy(p => p.TsName)) { url += (first2 ? '?' : '&') + p.TsName + "=${encodeURIComponent('' + " + p.TsName + ")}"; first2 = false; } writer.WriteLine($"let url = this._hostname + `{url}`;"); // Output parameter type conversions foreach (var p in method.Parameters) { OutputTypeConversion(writer, p.TsName, p.TsType, toTypeScript: false); } // Build request body var bodyParams = method.Parameters.Where(p => p.Location == ParameterLocation.RequestBody).OrderBy(p => p.TsName).ToList(); if (bodyParams.Count > 0 && httpMethod == "GET") { throw new InvalidOperationException($"GET requests must not have any body parameters. Offending parameter: {bodyParams[0].TsName}, method {method.Method.Name}, controller {s.Controller.FullName}"); } if (bodyParams.Count > 1 && (method.BodyEncoding == BodyEncoding.Raw || method.BodyEncoding == BodyEncoding.Json)) { throw new InvalidOperationException($"The body encoding for this method allows for at most one body parameter. Offending parameters: [{bodyParams.Select(p => p.TsName).JoinString(", ")}], method {method.Method.Name}, controller {s.Controller.FullName}"); } var bodyCode = ""; if (bodyParams.Count > 0) { if (method.BodyEncoding == BodyEncoding.Raw) { bodyCode = $", body: {bodyParams[0].TsName}"; } else if (method.BodyEncoding == BodyEncoding.Json) { bodyCode = $", body: JSON.stringify({bodyParams[0].TsName}), headers: {{ 'Content-Type': 'application/json' }}"; } else if (method.BodyEncoding == BodyEncoding.FormUrlEncoded) { writer.WriteLine("let __body = new URLSearchParams();"); foreach (var bp in bodyParams) { writer.WriteLine($"__body.append('{bp.TsName}', '' + {bp.TsName});"); } bodyCode = ", body: __body"; } else if (method.BodyEncoding == BodyEncoding.MultipartFormData) { writer.WriteLine("let __body = new FormData();"); foreach (var bp in bodyParams) { writer.WriteLine($"__body.append('{bp.TsName}', '' + {bp.TsName});"); } bodyCode = ", body: __body"; throw new NotImplementedException("FormData encoding is not fully implemented."); // no support for file name, parameter is always stringified with no support for Blob } else { throw new Exception($"Unexpected {nameof(method.BodyEncoding)}: {method.BodyEncoding}"); } } // Output call var sendCookies = s.SendCookies == SendCookies.Always ? "include" : s.SendCookies == SendCookies.SameOriginOnly ? "same-origin" : s.SendCookies == SendCookies.Never ? "omit" : throw new Exception(); if (canDirectReturn) { writer.Write("return "); } else { writer.Write("let result = await "); } writer.WriteLine($"this.{httpMethod}<{method.TsReturnType.GetTypeScript()}>(url, {{ credentials: '{sendCookies}'{bodyCode} }});"); if (!canDirectReturn) { // Output return type conversion OutputTypeConversion(writer, "result", method.TsReturnType, toTypeScript: true); writer.WriteLine("return result;"); } } writer.WriteLine("}"); writer.WriteLine(); } } // Output type converters foreach (var tc in _typeConverters.Values.Where(c => c != null).OrderBy(c => c.ForType.GetHash())) { if (_convertersUsedFrom.Contains(tc)) { writer.WriteLine($"private {tc.FunctionName(toTypeScript: false)}(val: {tc.ForType.GetTypeScript()}): any {{"); using (writer.Indent()) tc.WriteFunctionBody(writer, false); writer.WriteLine("}"); writer.WriteLine(); } if (_convertersUsedTo.Contains(tc)) { writer.WriteLine($"private {tc.FunctionName(toTypeScript: true)}(val: any): {tc.ForType.GetTypeScript()} {{"); using (writer.Indent()) tc.WriteFunctionBody(writer, true); writer.WriteLine("}"); writer.WriteLine(); } } } writer.WriteLine("}"); }