public static void CopyDtsFile(DefinitionMapData definitionMapData, ProjectItem projectItem, string dts, bool isEnumDefinition) { // There might be paths where this file should be copied to foreach (string copyPath in definitionMapData.CopyPaths) { // Ignore empty paths if (string.IsNullOrWhiteSpace(copyPath)) { continue; } // Get the path from our project item and combine it with the target path and target name string filePath = Path.GetFullPath(Path.Combine( Path.GetDirectoryName(projectItem.FileNames[1]), copyPath, GenerationService.GetCopyDtsFileName(definitionMapData, projectItem, isEnumDefinition))); // Try to write our definition file to the new path too try { File.WriteAllText(filePath, dts); VSHelpers.WriteOnOutputWindow($"File written to \"{filePath}\""); } catch (Exception ex) { VSHelpers.WriteOnOutputWindow($"Could not write file to \"{filePath}\"{Environment.NewLine}" + $"Reason: {ex.Message}"); } } }
public static string ConvertToTypeScriptEnumsOnly(ProjectItem sourceItem, ref DefinitionMapData definitionMapData, out bool isEmpty) { try { // Initialize the definition data if there was no specified if (definitionMapData == null) { definitionMapData = new DefinitionMapData(); } Options.ReadOptionOverrides(sourceItem); VSHelpers.WriteOnOutputWindow(string.Format("{0} - Started (enums only)", sourceItem.Name)); var list = IntellisenseParser.ProcessFile(sourceItem, definitionMapData); VSHelpers.WriteOnOutputWindow(string.Format("{0} - Completed", sourceItem.Name)); return(IntellisenseWriter.WriteTypeScriptEnumsOnly(list, sourceItem, out isEmpty)); } catch (Exception ex) { isEmpty = true; VSHelpers.WriteOnOutputWindow(string.Format("{0} - Failure", sourceItem.Name)); Telemetry.TrackException("ParseFailure", ex); return(null); } }
public static void CreateDtsMapFile(ProjectItem sourceItem, DefinitionMapData definitionMapData) { string sourceFile = sourceItem.FileNames[1]; string dtsFile = GenerationService.GenerateFileName(sourceFile) + ".map"; VSHelpers.CheckFileOutOfSourceControl(dtsFile); File.WriteAllText(dtsFile, JsonConvert.SerializeObject(definitionMapData, Formatting.Indented)); if (sourceItem.ContainingProject.IsKind(ProjectTypes.DOTNET_Core, ProjectTypes.ASPNET_5)) { Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { var dtsItem = VSHelpers.GetProjectItem(dtsFile); if (dtsItem != null) { dtsItem.Properties.Item("DependentUpon").Value = sourceItem.Name; } Telemetry.TrackOperation("FileGenerated"); }), DispatcherPriority.ApplicationIdle, null); } else if (sourceItem.ContainingProject.IsKind(ProjectTypes.WEBSITE_PROJECT)) { sourceItem.ContainingProject.ProjectItems.AddFromFile(dtsFile); } else { sourceItem.ProjectItems.AddFromFile(dtsFile); } }
internal static DefinitionMapData GetDefinitionMapData(ProjectItem projectItem) { // Initialize our result DefinitionMapData result = null; // Try to find our map data for the typescript definition file ProjectItem definitionMapProjectItem = projectItem.ProjectItems .Cast <ProjectItem>() .Where(pi => pi.Name == GenerationService.GenerateFileName(projectItem.Name) + ".map") .FirstOrDefault(); // Continue if found if (definitionMapProjectItem != null) { // Get the text of our mapping file string documentText = VSHelpers.GetDocumentText(definitionMapProjectItem); // Continue if the document has text if (string.IsNullOrWhiteSpace(documentText) == false) { // Try to parse the containing json string // When a SerializationException is getting thrown the document text wasn't valid try { result = JsonConvert.DeserializeObject <DefinitionMapData>(documentText); } catch (JsonSerializationException) { } } } return(result); }
public static void CreateDtsFile(ProjectItem sourceItem) { string sourceFile = sourceItem.FileNames[1]; string dtsFile = GenerationService.GenerateFileName(sourceFile); string dtsEnumFile = GenerationService.GenerateFileName(sourceFile); // Get metadata from our project item DefinitionMapData definitionMapData = VSHelpers.GetDefinitionMapData(sourceItem); string dts = ConvertToTypeScriptWithoutEnums(sourceItem, ref definitionMapData, out bool isEmpty); string dtsEnumOnly = ConvertToTypeScriptEnumsOnly(sourceItem, ref definitionMapData, out bool isEmptyEnum); VSHelpers.CheckFileOutOfSourceControl(dtsFile); File.WriteAllText(dtsFile, dts); if (isEmptyEnum == false) { File.WriteAllText(dtsEnumFile, dtsEnumOnly); } if (sourceItem.ContainingProject.IsKind(ProjectTypes.DOTNET_Core, ProjectTypes.ASPNET_5)) { Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { var dtsItem = VSHelpers.GetProjectItem(dtsFile); if (dtsItem != null) { dtsItem.Properties.Item("DependentUpon").Value = sourceItem.Name; } if (isEmptyEnum == false) { var dtsItem2 = VSHelpers.GetProjectItem(dtsEnumFile); if (dtsItem2 != null) { dtsItem2.Properties.Item("DependentUpon").Value = sourceItem.Name; } } Telemetry.TrackOperation("FileGenerated"); }), DispatcherPriority.ApplicationIdle, null); } else if (sourceItem.ContainingProject.IsKind(ProjectTypes.WEBSITE_PROJECT)) { sourceItem.ContainingProject.ProjectItems.AddFromFile(dtsFile); if (isEmptyEnum == false) { sourceItem.ContainingProject.ProjectItems.AddFromFile(dtsEnumFile); } } // Also create the definition map data and add it to our project item CreateDtsMapFile(sourceItem, definitionMapData); }
private static void ProcessClass(ProjectItem projectItem, DefinitionMapData definitionMapData, CodeClass cc, CodeClass baseClass, List <IntellisenseObject> list, HashSet <CodeClass> underProcess) { string baseNs = null; string baseClassName = null; string ns = GetNamespace(cc); string className = GetClassName(cc); HashSet <string> references = new HashSet <string>(); IList <IntellisenseProperty> properties = GetProperties(projectItem, definitionMapData, cc.Members, new HashSet <string>(), references).ToList(); foreach (CodeElement member in cc.Members) { if (ShouldProcess(member)) { ProcessElement(projectItem, definitionMapData, member, list, underProcess); } } if (baseClass != null) { baseClassName = GetClassName(baseClass); baseNs = GetNamespace(baseClass); // Some definitions may be defined inside of another project if (baseClass.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal) { // Try retrieving the external codeclass by walking all references the current project has if (TryGetExternalType(projectItem, definitionMapData, baseClass.FullName, out CodeClass2 codeClass, out CodeEnum codeEnum)) { if (codeClass != null) { HasIntellisense(codeClass.ProjectItem, references); } if (codeEnum != null) { HasIntellisense(codeEnum.ProjectItem, references); } } } else { HasIntellisense(baseClass.ProjectItem, references); } } var intellisenseObject = new IntellisenseObject(properties.ToList(), references) { Namespace = ns, Name = className, BaseNamespace = baseNs, BaseName = baseClassName, FullName = cc.FullName, Summary = GetSummary(cc) }; list.Add(intellisenseObject); }
public static string GetCopyDtsFileName(DefinitionMapData definitionMapData, ProjectItem projectItem, bool isEnumDefinition) { string sourceFile = string.IsNullOrWhiteSpace(definitionMapData.CustomName) ? projectItem.Name : definitionMapData.CustomName; if (isEnumDefinition) { sourceFile += "Enum"; } return(GenerationService.GenerateFileName(sourceFile)); }
private static void ProcessElement(ProjectItem projectItem, DefinitionMapData definitionMapData, CodeElement element, List <IntellisenseObject> list, HashSet <CodeClass> underProcess) { if (element.Kind == vsCMElement.vsCMElementEnum) { ProcessEnum((CodeEnum)element, list); } else if (element.Kind == vsCMElement.vsCMElementClass) { var cc = (CodeClass)element; // Don't re-generate the intellisense. if (list.Any(x => x.Name == GetClassName(cc) && x.Namespace == GetNamespace(cc))) { return; } // Collect inherit classes. CodeClass baseClass = null; try { // To recuse from throwing from a weird case // where user inherit class from struct and save. As such inheritance is disallowed. baseClass = cc.Bases.Cast <CodeClass>() .FirstOrDefault(c => c.FullName != "System.Object"); } catch { /* Silently continue. */ } ProcessClass(projectItem, definitionMapData, cc, baseClass, list, underProcess); var references = new HashSet <string>(); try { // Process Inheritence. if (baseClass != null && !underProcess.Contains(baseClass) && !HasIntellisense(baseClass.ProjectItem, references)) { list.Last().UpdateReferences(references); underProcess.Add(baseClass); list.AddRange(ProcessFile(baseClass.ProjectItem, definitionMapData, underProcess)); } } catch { } } }
protected override byte[] GenerateCode(string inputFileName, string inputFileContent) { ProjectItem item = Dte.Solution.FindProjectItem(inputFileName); this.originalExt = Path.GetExtension(inputFileName); if (item != null) { try { // Get metadata from our project item DefinitionMapData definitionMapData = VSHelpers.GetDefinitionMapData(item); string dts = GenerationService.ConvertToTypeScriptWithoutEnums(item, ref definitionMapData, out bool isEmpty); string dtsEnum = GenerationService.ConvertToTypeScriptEnumsOnly(item, ref definitionMapData, out bool isEmptyEnum); Telemetry.TrackOperation("FileGenerated"); if (isEmpty == false) { // Copy our dts file to the specified paths in the definition map data GenerationService.CopyDtsFile(definitionMapData, item, dts, false); } if (isEmptyEnum == false) { GenerationService.CopyDtsFile(definitionMapData, item, dtsEnum, true); GenerationService.CreateEnumFile(item, dtsEnum); } // And in the last step write the map file which contains some metadata GenerationService.CreateDtsMapFile(item, definitionMapData); return(Encoding.UTF8.GetBytes(dts)); } catch (Exception ex) { Telemetry.TrackOperation("FileGenerated", Microsoft.VisualStudio.Telemetry.TelemetryResult.Failure); Telemetry.TrackException("FileGenerated", ex); } } return(new byte[0]); }
public static string WriteTypeScriptWithoutEnums(IEnumerable <IntellisenseObject> objects, ProjectItem sourceItem, out bool isEmpty) { isEmpty = true; var sb = new StringBuilder(); foreach (var ns in objects.GroupBy(o => o.Namespace)) { List <string> references = GetReferences(objects, sourceItem); if (references.Count > 0) { foreach (string referencePath in references.OrderBy(p => Path.GetFileName(p))) { string path = Path.GetFileName(referencePath); ProjectItem definitionMapProjectItem = sourceItem.DTE.Solution.FindProjectItem(referencePath); if (definitionMapProjectItem != null) { DefinitionMapData definitionMapData = VSHelpers.GetDefinitionMapData(definitionMapProjectItem.Collection.Parent as ProjectItem); if (definitionMapData != null) { if (string.IsNullOrWhiteSpace(definitionMapData.CustomName) == false) { path = GenerationService.GetCopyDtsFileName(definitionMapData, definitionMapProjectItem, false); } } } sb.AppendFormat("/// <reference path=\"{0}\" />\r\n", path); } sb.AppendLine(); } if (!Options.GlobalScope) { sb.AppendFormat("declare module {0} {{\r\n", ns.Key); } foreach (IntellisenseObject io in ns) { if (io.IsEnum) { continue; } isEmpty = false; if (!string.IsNullOrEmpty(io.Summary)) { sb.AppendLine("\t/** " + _whitespaceTrimmer.Replace(io.Summary, "") + " */"); } string type = Options.ClassInsteadOfInterface ? "\tclass " : "\tinterface "; sb.Append(type).Append(Utility.CamelCaseClassName(io.Name)).Append(" "); if (!string.IsNullOrEmpty(io.BaseName)) { sb.Append("extends "); if (Options.GlobalScope == false) { sb.Append(ns.Key).Append("."); } if (!string.IsNullOrEmpty(io.BaseNamespace) && io.BaseNamespace != io.Namespace) { sb.Append(io.BaseNamespace).Append("."); } sb.Append(Utility.CamelCaseClassName(io.BaseName)).Append(" "); } WriteTSInterfaceDefinition(sb, "\t", io.Properties); sb.AppendLine(); } if (!Options.GlobalScope) { sb.AppendLine("}"); } } return(sb.ToString()); }
public static string WriteTypeScriptEnumsOnly(IEnumerable <IntellisenseObject> objects, ProjectItem sourceItem, out bool isEmpty) { isEmpty = true; var sb = new StringBuilder(); foreach (var ns in objects.GroupBy(o => o.Namespace)) { List <string> references = GetReferences(objects, sourceItem); if (references.Count > 0) { foreach (string referencePath in references.OrderBy(p => Path.GetFileName(p))) { string path = Path.GetFileName(referencePath); ProjectItem definitionMapProjectItem = sourceItem.DTE.Solution.FindProjectItem(referencePath); if (definitionMapProjectItem != null) { DefinitionMapData definitionMapData = VSHelpers.GetDefinitionMapData(definitionMapProjectItem.Collection.Parent as ProjectItem); if (definitionMapData != null) { if (string.IsNullOrWhiteSpace(definitionMapData.CustomName) == false) { path = GenerationService.GetCopyDtsFileName(definitionMapData, definitionMapProjectItem, true); } } } sb.AppendFormat("/// <reference path=\"{0}\" />\r\n", path); } sb.AppendLine(); } if (!Options.GlobalScope) { sb.AppendFormat("declare module {0} {{\r\n", ns.Key); } foreach (IntellisenseObject io in ns) { if (io.IsEnum == false) { continue; } isEmpty = false; if (!string.IsNullOrEmpty(io.Summary)) { sb.AppendLine("\t/** " + _whitespaceTrimmer.Replace(io.Summary, "") + " */"); } sb.AppendLine("\tenum " + Utility.CamelCaseClassName(io.Name) + " {"); foreach (var p in io.Properties) { WriteTypeScriptComment(p, sb); if (p.InitExpression != null) { sb.AppendLine("\t\t" + Utility.CamelCaseEnumValue(p.Name) + " = " + CleanEnumInitValue(p.InitExpression) + ","); } else { sb.AppendLine("\t\t" + Utility.CamelCaseEnumValue(p.Name) + ","); } } sb.AppendLine("\t}"); } if (!Options.GlobalScope) { sb.AppendLine("}"); } } return(sb.ToString()); }
private static bool TryGetExternalTypeFromMetadata(ProjectItem projectItem, DefinitionMapData definitionMapData, string fullName, out CodeClass2 codeClass, out CodeEnum codeEnum) { // Initialize the out parameter with null codeClass = null; codeEnum = null; try { // Only continue if metadata was specified if (definitionMapData != null) { if (projectItem.ContainingProject != null && projectItem.ContainingProject.Object is VSProject vsproject) { // Try to get the metadata for the external type we search for ReferenceMetadata referenceMetadata = definitionMapData.ReferenceMetadata.Where(md => md.TypeName == fullName).FirstOrDefault(); if (referenceMetadata != null) { // Get the project reference from the info specified in the metadata Reference projectReference = vsproject.References.Cast <Reference>() .Where(r => r.SourceProject != null && r.Name == referenceMetadata.ProjectName).FirstOrDefault(); // If the reference wasn't found we can remove the metadata from the definition map file since it's no longer valid if (projectReference == null) { definitionMapData.ReferenceMetadata.Remove(referenceMetadata); } else { // Get the project item which holds our type from the info specified in the metadata ProjectItem projectReferenceItem = projectReference.SourceProject.ProjectItems.Cast <ProjectItem>() .Where(pi => pi.FileCodeModel != null && pi.Name == referenceMetadata.ProjectItemName).FirstOrDefault(); // If the project item wasn't found we can remove the metadata from the definition map file since it's no longer valid if (projectReferenceItem == null) { definitionMapData.ReferenceMetadata.Remove(referenceMetadata); } else { // Iterate the project items code elements foreach (CodeElement element in projectReferenceItem.FileCodeModel.CodeElements) { // Skip if the element is not a namespace if (element.Kind != vsCMElement.vsCMElementNamespace) { continue; } if (element is CodeNamespace externalCodeNamespace) { // Iterate the namespace members foreach (CodeElement member in externalCodeNamespace.Members) { // Return the found class if the fullname of the internal and external classes matches if (member.Kind == vsCMElement.vsCMElementClass && member is CodeClass2 externalCodeClass && externalCodeClass.FullName == fullName) { codeClass = externalCodeClass; return(true); } // Return the found enum if the fullname of the internal and external enums matches else if (member.Kind == vsCMElement.vsCMElementEnum && member is CodeEnum externalCodeEnum && externalCodeEnum.FullName == fullName) { codeEnum = externalCodeEnum; return(true); } } } } } } } } }
private static IntellisenseType GetType(ProjectItem projectItem, DefinitionMapData definitionMapData, CodeClass rootElement, CodeTypeRef codeTypeRef, HashSet <string> traversedTypes, HashSet <string> references) { var isArray = codeTypeRef.TypeKind == vsCMTypeRef.vsCMTypeRefArray; var isCollection = codeTypeRef.AsString.StartsWith("System.Collections", StringComparison.Ordinal); var isNullable = codeTypeRef.AsFullName.StartsWith("System.Nullable", StringComparison.Ordinal); var isDictionary = false; var effectiveTypeRef = codeTypeRef; if (isArray && codeTypeRef.ElementType != null) { effectiveTypeRef = effectiveTypeRef.ElementType; } else if (isCollection || isNullable) { effectiveTypeRef = TryToGuessGenericArgument(rootElement, effectiveTypeRef); } if (isCollection) { isDictionary = codeTypeRef.AsString.StartsWith("System.Collections.Generic.Dictionary", StringComparison.Ordinal); } string typeName = effectiveTypeRef.AsFullName; try { var codeClass = effectiveTypeRef.CodeType as CodeClass2; var codeEnum = effectiveTypeRef.CodeType as CodeEnum; var isPrimitive = IsPrimitive(effectiveTypeRef); // Some definitions may be defined inside of another project if (Options.AssumeExternalType == false && (codeClass != null || codeEnum != null) && effectiveTypeRef.TypeKind == vsCMTypeRef.vsCMTypeRefCodeType && effectiveTypeRef.CodeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal) { // Try retrieving the external codeclass by walking all references the current project has if (TryGetExternalType(projectItem, definitionMapData, codeClass != null ? codeClass.FullName : codeEnum.FullName, out CodeClass2 externalCodeClass, out CodeEnum externalCodeEnum)) { // If successful use the new type codeClass = externalCodeClass; codeEnum = externalCodeEnum; } } var result = new IntellisenseType { IsArray = !isDictionary && (isArray || isCollection), IsDictionary = isDictionary, IsOptional = isNullable, CodeName = effectiveTypeRef.AsString }; if (effectiveTypeRef.TypeKind == vsCMTypeRef.vsCMTypeRefCodeType && (effectiveTypeRef.CodeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject || (Options.AssumeExternalType == false && effectiveTypeRef.CodeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal))) { try { result.ClientSideReferenceName = (codeClass != null && HasIntellisense(codeClass.ProjectItem, references) ? (GetNamespace(codeClass) + "." + Utility.CamelCaseClassName(GetClassName(codeClass))) : null) ?? (codeEnum != null && HasIntellisense(codeEnum.ProjectItem, references) ? (GetNamespace(codeEnum) + "." + Utility.CamelCaseClassName(codeEnum.Name)) : null); } catch (Exception) { } } else if (Options.AssumeExternalType && effectiveTypeRef.TypeKind == vsCMTypeRef.vsCMTypeRefCodeType && effectiveTypeRef.CodeType.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal) { try { result.ClientSideReferenceName = (codeClass != null ? (GetNamespace(codeClass) + "." + Utility.CamelCaseClassName(GetClassName(codeClass))) : null) ?? (codeEnum != null ? (GetNamespace(codeEnum) + "." + Utility.CamelCaseClassName(codeEnum.Name)) : null); } catch (Exception) { } } else { result.ClientSideReferenceName = null; } if (!isPrimitive && codeClass != null && !traversedTypes.Contains(effectiveTypeRef.CodeType.FullName) && !isCollection) { traversedTypes.Add(effectiveTypeRef.CodeType.FullName); result.Shape = GetProperties(projectItem, definitionMapData, effectiveTypeRef.CodeType.Members, traversedTypes, references).ToList(); traversedTypes.Remove(effectiveTypeRef.CodeType.FullName); } return(result); } catch (InvalidCastException) { VSHelpers.WriteOnOutputWindow(string.Format("ERROR - Cannot find definition for {0}", typeName)); throw new ArgumentException(string.Format("Cannot find definition of {0}", typeName)); } }
//internal static class Ext //{ // public const string TypeScript = ".d.ts"; //} internal static IEnumerable <IntellisenseObject> ProcessFile(ProjectItem item, DefinitionMapData definitionMapData, HashSet <CodeClass> underProcess = null) { if (item.FileCodeModel == null || item.ContainingProject == null) { return(null); } _project = item.ContainingProject; List <IntellisenseObject> list = new List <IntellisenseObject>(); if (underProcess == null) { underProcess = new HashSet <CodeClass>(); } foreach (CodeElement element in item.FileCodeModel.CodeElements) { if (element.Kind == vsCMElement.vsCMElementNamespace) { CodeNamespace cn = (CodeNamespace)element; foreach (CodeElement member in cn.Members) { if (ShouldProcess(member)) { ProcessElement(item, definitionMapData, member, list, underProcess); } } } else if (ShouldProcess(element)) { ProcessElement(item, definitionMapData, element, list, underProcess); } } return(new HashSet <IntellisenseObject>(list)); }
private static IEnumerable <IntellisenseProperty> GetProperties(ProjectItem projectItem, DefinitionMapData definitionMapData, CodeElements props, HashSet <string> traversedTypes, HashSet <string> references = null) { return(from p in props.OfType <CodeProperty>() where !p.Attributes.Cast <CodeAttribute>().Any(HasIgnoreAttribute) where vsCMAccess.vsCMAccessPublic == p.Access && p.Getter != null && !p.Getter.IsShared && IsPublic(p.Getter) select new IntellisenseProperty { Name = GetName(p), Type = GetType(projectItem, definitionMapData, p.Parent, p.Type, traversedTypes, references), Summary = GetSummary(p) }); }