예제 #1
0
        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);
        }
예제 #2
0
        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}.");
            }
        }
예제 #3
0
        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));
        }
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #6
0
        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));
        }
예제 #7
0
        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)));
            }
        }
예제 #8
0
        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));
        }
예제 #9
0
        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));
        }