public async Task <bool> VerifyAsync(TypeBuilderConfig typeBuilderConfig, Config config, GeneratorContext generatorContext) { if (!File.Exists(FilePath)) { Program.WriteError($"File {FilePath} does not exist."); return(false); } try { var origContent = await File.ReadAllTextAsync(FilePath); var newContent = await GetContentAsync(typeBuilderConfig, config, generatorContext); if (newContent.Replace("\r\n", "\n") != origContent.Replace("\r\n", "\n")) { Program.WriteError($"Generated file {FilePath} does not match the source definition. Run the frontend build and commit all changes to generated types."); return(false); } } catch (Exception ex) { Program.WriteError($"Error verifying generated file {FilePath}: {ex.Message}"); return(false); } return(true); }
public async Task ApplyAsync(TypeBuilderConfig typeBuilderConfig, Config config, GeneratorContext generatorContext) { bool exists = File.Exists(FilePath); var origContent = exists ? ReadAllTextSafe(FilePath) : ""; var newContent = await GetContentAsync(typeBuilderConfig, config, generatorContext); if (newContent != origContent) { await File.WriteAllTextAsync(FilePath, newContent); Console.WriteLine($"{(exists ? "Updated" : "Created")} file {FilePath}."); } }
private static DerivedTypesUnionGeneration GetDerivedTypes(Type type, TypeBuilderConfig config, string currentTsNamespace, GeneratorContext generatorContext) { var derivedTypesUnionName = GetDerivedTypesUnionName(type); if (derivedTypesUnionName == null) { return(null); } var derivedClasses = generatorContext.FindDerivedTypes(type); var derivedTypes = derivedClasses .Where(t => !t.IsAbstract) .OrderBy(t => t.Name, StringComparer.InvariantCulture); return(new DerivedTypesUnionGeneration(ImmutableArray.CreateRange(derivedTypes.Select(i => BuildTsTypeReference(i, config, currentTsNamespace, true))), derivedTypesUnionName)); }
private void CreateTypeBuilderConfig() { var typeMappings = new Dictionary <string, TsTypeReference>(); void AddTypeMappingsFromAssembly(Assembly asm) { foreach (var attribute in TypeUtils.GetAssemblyCustomAttributesData(asm).Where(a => a.AttributeType.Name == Constants.DefineTypeScriptTypeForExternalTypeAttributeName && a.ConstructorArguments.Count == 2 && a.ConstructorArguments[1].Value is string)) { string key; if (attribute.ConstructorArguments[0].Value is Type type) { key = TypeUtils.GetFullNameWithGenericArguments(type); } else if (attribute.ConstructorArguments[0].Value is string s) { key = s; } else { continue; } typeMappings[key] = TsTypeReference.Simple((string)attribute.ConstructorArguments[1].Value); } } foreach (var assembly in _generatorContext.Assemblies) { AddTypeMappingsFromAssembly(assembly); } // override mappings from config foreach (var mapping in _config.TypeMappings) { var reference = TsTypeReference.Simple(mapping.Value); typeMappings[mapping.Key] = reference; } var mappings = ImmutableDictionary.CreateRange(typeMappings); _typeBuilderConfig = new TypeBuilderConfig(mappings, _config.CustomTypeScriptIgnoreAttributeFullName, _config.WrapConstEnumsInTemplateStrings); }
protected async Task <string> GetContentAsync(TypeBuilderConfig typeBuilderConfig, Config config, GeneratorContext generatorContext) { bool first = true; var innerSource = new StringBuilder(); foreach (var t in _types.OrderBy(t => t.Name, StringComparer.InvariantCulture).ThenBy(t => t.FullName, StringComparer.InvariantCulture)) { if (!first) { innerSource.Append(config.NewLine); } var tsTypeDefinition = await TypeBuilder.BuildTsTypeDefinitionAsync(t, typeBuilderConfig, generatorContext); innerSource.Append(tsTypeDefinition.GetSource(FilePath, config, generatorContext)); first = false; } return("declare namespace " + _namespaceName + " {" + config.NewLine + innerSource + "}" + config.NewLine); }
private static TsInterfaceMember BuildMember(MemberInfo member, IList <Type> interfaces, TypeBuilderConfig config, string currentTsNamespace) { var interfaceProperties = interfaces.SelectMany(i => TypeUtils.GetRelevantAndBaseProperties(i).Where(p => p.Name == member.Name)); var allPropertiesToCheckForIgnore = new List <MemberInfo>(); allPropertiesToCheckForIgnore.Add(member); allPropertiesToCheckForIgnore.AddRange(interfaceProperties); var currentType = member.DeclaringType?.BaseType; while (currentType != null) { var baseProperties = TypeUtils.GetRelevantAndBaseProperties(currentType).Where(p => p.Name == member.Name); allPropertiesToCheckForIgnore.AddRange(baseProperties); currentType = currentType.BaseType; } foreach (var propertyToCheckForIgnore in allPropertiesToCheckForIgnore) { var attributes = TypeUtils.GetCustomAttributesData(propertyToCheckForIgnore); if (attributes.All(a => a.AttributeType.Name != Constants.TypeScriptTypeAttributeName)) { if (attributes.Any(a => a.AttributeType.FullName == "Newtonsoft.Json.JsonIgnoreAttribute")) { return(null); } if (attributes.Any(a => a.AttributeType.FullName == config.CustomTypeScriptIgnoreAttributeFullName)) { return(null); } if (attributes.Any(a => a.AttributeType.Name == Constants.TypeScriptIgnoreAttributeName)) { return(null); } } } string name = FindNameFromJsonPropertyAttribute(member); if (string.IsNullOrEmpty(name)) { name = StringUtils.ToCamelCase(member.Name); } var membersToCheckForOptional = new List <MemberInfo> { member }; membersToCheckForOptional.AddRange(member.DeclaringType.GetInterfaces().SelectMany(i => i.GetMember(member.Name)).Where(m => m != null)); var isOptional = membersToCheckForOptional.SelectMany(m => TypeUtils.GetCustomAttributesData(m)).FirstOrDefault(a => a.AttributeType.Name == Constants.TypeScriptOptionalAttributeName) != null; return(new TsInterfaceMember(name, BuildTsTypeReferenceToPropertyType(member, config, currentTsNamespace, isOptional), member, isOptional)); }
public static async Task <TsTypeDefinition> BuildTsTypeDefinitionAsync(Type type, TypeBuilderConfig config, GeneratorContext generatorContext) { var tsNamespace = GetTypescriptNamespace(type); if (type.IsEnum) { var members = Enum.GetNames(type); return(TsTypeDefinition.Enum(type.Name, members)); } else { var properties = TypeUtils.GetRelevantProperties(type); var fields = TypeUtils.GetRelevantFields(type); IList <Type> extends; if (type.IsInterface) { extends = type.GetInterfaces(); } else if (type.IsValueType || TypeUtils.Is <object>(type.BaseType)) { extends = new List <Type>(); } else { extends = new List <Type> { type.BaseType }; } foreach (var iface in type.GetInterfaces()) { // We look for default interface properties because we don't generate an extends clause for C# interfaces // but that means we loose default interface properties. If any of the types interface has default properties // we include it in the extends clause. var defaultInterfaceProperties = TypeUtils.GetRelevantProperties(iface).Where(p => p.GetGetMethod()?.IsAbstract == false); properties.AddRange(defaultInterfaceProperties); } var parentToAugument = GetParentTypeToAugument(type); var parentDefToAugument = default(TsTypeDefinition); if (parentToAugument != null) { parentDefToAugument = await BuildTsTypeDefinitionAsync(parentToAugument, config, generatorContext); } var members = new List <TsInterfaceMember>(); members.AddRange(properties.Select(m => BuildMember(m, type.GetInterfaces(), config, tsNamespace)).Where(x => x != null)); members.AddRange(fields.Select(m => BuildMember(m, type.GetInterfaces(), config, tsNamespace)).Where(x => x != null)); return(TsTypeDefinition.Interface(type, members, extends.Select(e => BuildTsTypeReference(e, config, tsNamespace, true)), type.GetTypeInfo().GenericTypeParameters.Select(TypeUtils.GetNameWithoutGenericArity), GetDerivedTypes(type, config, tsNamespace, generatorContext), parentDefToAugument, GetTypeMemberName(type))); } }
private static TsTypeReference BuildTsTypeReference(Type type, TypeBuilderConfig config, string currentTsNamespace, bool returnTheMainTypeEvenIfItHasADerivedTypesUnion) { var isOptional = false; var underlyingNullableType = GetUnderlyingNullableType(type); if (underlyingNullableType != null) { type = underlyingNullableType; isOptional = true; } if (type.IsGenericParameter) { return(TsTypeReference.Simple(type.Name, isOptional)); } var typeFullName = TypeUtils.GetFullNameWithGenericArguments(type); if (typeFullName != null && config.TypeMappings.TryGetValue(typeFullName, out var mappedType)) { return(TsTypeReference.Wrap(mappedType, isOptional)); } var typeScriptTypeAttribute = GetTypeScriptTypeAttribute(type); if (typeScriptTypeAttribute != null) { if (typeScriptTypeAttribute.ConstructorArguments[0].Value is string typeString) { return(TsTypeReference.Simple(typeString)); } else if (typeScriptTypeAttribute.ConstructorArguments[0].Value is Type replaceWithType) { type = replaceWithType; } } if ( TypeUtils.Is <byte>(type) || TypeUtils.Is <sbyte>(type) || TypeUtils.Is <int>(type) || TypeUtils.Is <uint>(type) || TypeUtils.Is <long>(type) || TypeUtils.Is <ulong>(type) || TypeUtils.Is <short>(type) || TypeUtils.Is <ushort>(type) || TypeUtils.Is <double>(type) || TypeUtils.Is <decimal>(type) || TypeUtils.Is <float>(type) ) { return(TsTypeReference.Simple("number", isOptional)); } if (TypeUtils.Is <string>(type)) { return(TsTypeReference.Simple("string", isOptional)); } if (TypeUtils.Is <bool>(type)) { return(TsTypeReference.Simple("boolean", isOptional)); } var dictionaryTypes = GetDictionaryUnderlyingTypes(type); if (dictionaryTypes != null) { return(TsTypeReference.Dictionary(BuildTsTypeReference(dictionaryTypes.Item1, config, currentTsNamespace, false), BuildTsTypeReference(dictionaryTypes.Item2, config, currentTsNamespace, false))); } var enumerableUnderlyingType = GetEnumerableUnderlyingType(type); if (enumerableUnderlyingType != null) { return(TsTypeReference.Array(BuildTsTypeReference(enumerableUnderlyingType, config, currentTsNamespace, false))); } if (typeFullName != null && (type.IsClass || type.IsInterface || type.IsValueType)) { var namespaceName = GetTypescriptNamespace(type); if (!string.IsNullOrEmpty(namespaceName)) { var derivedTypesUnionName = returnTheMainTypeEvenIfItHasADerivedTypesUnion ? null : GetDerivedTypesUnionName(type); var typeName = TypeUtils.GetNameWithoutGenericArity(type); var prefix = ""; var suffix = ""; if (type.IsEnum && config.WrapConstEnumsInTemplateStrings) { prefix = "`${"; suffix = "}`"; } var result = default(TsTypeReference); if (namespaceName == currentTsNamespace) { result = TsTypeReference.Simple(prefix + (derivedTypesUnionName ?? typeName) + suffix, isOptional); } else { result = TsTypeReference.Simple(prefix + namespaceName + "." + (derivedTypesUnionName ?? typeName) + suffix, isOptional); } if (result != null) { if (type.GenericTypeArguments.Length > 0) { var typeArgs = type.GenericTypeArguments.Select(t => BuildTsTypeReference(t, config, currentTsNamespace, false)); result = TsTypeReference.Generic(result, typeArgs); } return(result); } } } return(TsTypeReference.Simple("unknown", isOptional)); }
private static TsTypeReference BuildTsTypeReferenceToPropertyType(MemberInfo member, TypeBuilderConfig config, string currentTsNamespace, bool isOptional) { if (member is FieldInfo fieldInfo && fieldInfo.IsLiteral) { var typeValue = default(string); var constantValue = fieldInfo.GetRawConstantValue(); if (constantValue is string s) { typeValue = $"'{s}'"; } else if (constantValue is double d) { typeValue = d.ToString(CultureInfo.InvariantCulture); } else if (constantValue is float f) { typeValue = f.ToString(CultureInfo.InvariantCulture); } else if (constantValue is int i) { typeValue = i.ToString(CultureInfo.InvariantCulture); } else if (constantValue is long l) { typeValue = l.ToString(CultureInfo.InvariantCulture); } if (typeValue != default) { return(TsTypeReference.Simple(typeValue, isOptional)); } } var type = member is PropertyInfo propertyInfo ? propertyInfo.PropertyType : ((FieldInfo)member).FieldType; var typeScriptTypeAttribute = GetTypeScriptTypeAttribute(member); if (typeScriptTypeAttribute != null) { if (typeScriptTypeAttribute.ConstructorArguments[0].Value is string typeString) { return(TsTypeReference.Simple(typeString)); } else if (typeScriptTypeAttribute.ConstructorArguments[0].Value is Type replaceWithType) { type = replaceWithType; } } return(BuildTsTypeReference(type, config, currentTsNamespace, false)); }