private void GetActions(HtmlNode node, HtmlNode parent) { string controlConverterType = "NonWebFormsControl"; if (SupportedControls.ControlRulesMap.ContainsKey(node.Name)) { var conversionAction = new ControlConversionAction(node, parent, SupportedControls.ControlRulesMap[node.Name]); controlConverterType = conversionAction.ControlConverter.GetType().Name; _controlActions.Add(conversionAction); } else if (SupportedControls.UserControls.UserControlRulesMap.ContainsKey(node.Name)) { var conversionAction = new ControlConversionAction(node, parent, SupportedControls.UserControls.UserControlRulesMap[node.Name]); controlConverterType = conversionAction.ControlConverter.GetType().Name; _controlActions.Add(conversionAction); } else { Match aspControlTagRegex = AspControlsRegex.Match(node.Name); if (aspControlTagRegex.Success) { controlConverterType = UnSupportedControlConverter; } } _metricsContext.CollectActionMetrics(WebFormsActionType.ControlConversion, controlConverterType, node.Name); }
public override Task <IEnumerable <FileInformation> > MigrateClassAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.ClassConversion, ActionName); // NOTE: Removed temporarily until usings can be better determined, at the moment, too // many are being removed //var requiredNamespaces = _sourceFileSemanticModel.GetNamespacesReferencedByType(_originalDeclarationSyntax); //var namespaceNames = requiredNamespaces.Select(namespaceSymbol => namespaceSymbol.ToDisplayString()).Append(Constants.BlazorComponentsNamespace); var requiredNamespaces = _sourceFileSemanticModel.GetOriginalUsingNamespaces().Append(Constants.BlazorComponentsNamespace); requiredNamespaces = CodeSyntaxHelper.RemoveFrameworkUsings(requiredNamespaces); var usingStatements = CodeSyntaxHelper.BuildUsingStatements(requiredNamespaces); var modifiedClass = ((ClassDeclarationSyntax)_originalDeclarationSyntax) // Remove outdated base type references // TODO: Scan and remove specific base types in the future .ClearBaseTypes() // ComponentBase base class is added because user controls become // normal razor components .AddBaseType(Constants.ComponentBaseClass); var namespaceNode = CodeSyntaxHelper.BuildNamespace(_originalClassSymbol.ContainingNamespace?.ToDisplayString(), modifiedClass); DoCleanUp(); LogEnd(); var newRelativePath = GetNewRelativePath(); var fileContent = CodeSyntaxHelper.GetFileSyntaxAsString(namespaceNode, usingStatements); var result = new[] { new FileInformation(newRelativePath, Encoding.UTF8.GetBytes(fileContent)) }; return(Task.FromResult((IEnumerable <FileInformation>)result)); }
public override Task <IEnumerable <FileInformation> > MigrateClassAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.ClassConversion, ActionName); // NOTE: We could just read the file from the disk and retrieve the bytes like // that but instead I opted to "rebuild" the type in case we wanted to add comments // or something else to these undefined code files, most likely though we may still // want to scan parts of these files and remove/alter/take note of certain lines/info var newRelativePath = FilePathHelper.RemoveDuplicateDirectories(FilePathHelper.AlterFileName(_relativePath, newFileName: _originalClassSymbol.Name)); // NOTE: Removed temporarily until usings can be better determined, at the moment, too // many are being removed //var sourceClassComponents = GetSourceClassComponents(); var namespaceNames = _sourceFileSemanticModel.GetOriginalUsingNamespaces().Append(Constants.BlazorComponentsNamespace); namespaceNames = CodeSyntaxHelper.RemoveFrameworkUsings(namespaceNames); var usingStatements = CodeSyntaxHelper.BuildUsingStatements(namespaceNames); var namespaceNode = CodeSyntaxHelper.BuildNamespace(_originalClassSymbol.ContainingNamespace?.ToDisplayString(), _originalDeclarationSyntax); var fileText = CodeSyntaxHelper.GetFileSyntaxAsString(namespaceNode, usingStatements); DoCleanUp(); LogEnd(); var result = new[] { new FileInformation(newRelativePath, Encoding.UTF8.GetBytes(fileText)) }; return(Task.FromResult((IEnumerable <FileInformation>)result)); }
public override Task <IEnumerable <FileInformation> > MigrateFileAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.FileConversion, ChildActionType); // TODO: Extract info from project files and // call _blazorWorkspaceManager.CreateProjectFile // and _webFormsWorkspaceManager.CreateProjectFile // TODO: Retrieve cancellation token from thread manager service // _blazorWorkspaceManager.WaitUntilAllDocumentsInWorkspace(token); // TODO: Extract accumulated project info from // workspace and use it to build the actual // project file string newCsProjContent = GenerateProjectFileContents(_projectAnalyzer.ProjectResult, _projectAnalyzer.ProjectConfiguration, _projectAnalyzer.ProjectReferences, _projectAnalyzer.MetaReferences); FileInformation fi = new FileInformation(FilePathHelper.RemoveDuplicateDirectories(RelativePath), Encoding.UTF8.GetBytes(newCsProjContent)); var fileList = new List <FileInformation>() { fi }; DoCleanUp(); LogEnd(); return(Task.FromResult((IEnumerable <FileInformation>)fileList)); }
public static string ConstructBlazorDirectives(string content, string originalFilePath, string projectName, ViewImportService viewImportService, WebFormMetricContext metricContext) { var directiveName = DirectiveNameRegex.Match(content).Groups[DirectiveNameRegexGroupName].Value; var directiveConverter = SupportedControls.DirectiveRulesMap.ContainsKey(directiveName) ? SupportedControls.DirectiveRulesMap[directiveName] : SupportedControls.DefaultDirectiveConverter; metricContext.CollectActionMetrics(WebFormsActionType.DirectiveConversion, directiveName + "DirectiveConverter"); return(directiveConverter.ConvertDirective(directiveName, content.Trim(), originalFilePath, projectName, viewImportService)); }
public override async Task <IEnumerable <FileInformation> > MigrateClassAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.ClassConversion, ActionName); // Make this call once now so we don't have to keep doing it later var originalDescendantNodes = _originalDeclarationSyntax.DescendantNodes(); // Currently not implementing service layer so add blank line with a comment instead var configureServicesLines = new[] { CodeSyntaxHelper.GetBlankLine().AddComment(string.Format(Constants.OperationUnattemptedCommentTemplate, MigrateServiceLayerOperation), isLeading: false) }; // Iterate through methods and process them as needed ProcessMethods(originalDescendantNodes.OfType <MethodDeclarationSyntax>()); // With middleware lambdas added we need to retrieve registrations // before we have all statements required to build startup class await InsertRequestPipelineMiddlewareRegistrations(); var fileText = string.Empty; try { var startupClassDeclaration = StartupSyntaxHelper.ConstructStartupClass( constructorAdditionalStatements: originalDescendantNodes.OfType <ConstructorDeclarationSyntax>().FirstOrDefault()?.Body?.Statements, configureAdditionalStatements: _configureMethodStatements, configureServicesAdditionalStatements: configureServicesLines, additionalFieldDeclarations: originalDescendantNodes.OfType <FieldDeclarationSyntax>(), additionalPropertyDeclarations: originalDescendantNodes.OfType <PropertyDeclarationSyntax>(), additionalMethodDeclarations: _keepableMethods) .AddClassBlockComment(_endOfClassComments, false); var containingNamespace = CodeSyntaxHelper.BuildNamespace(_originalClassSymbol.ContainingNamespace?.ToDisplayString(), startupClassDeclaration); fileText = CodeSyntaxHelper.GetFileSyntaxAsString(containingNamespace, await GetAllUsingStatements()); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to construct new Startup file content from {OriginalClassName} class at {_fullPath}"); } DoCleanUp(); LogEnd(); // Global.asax.cs turns into Startup.cs var newRelativePath = FilePathHelper.RemoveDuplicateDirectories(Path.Combine(Path.GetDirectoryName(_relativePath), Constants.StartupFileName)); return(new[] { new FileInformation(newRelativePath, Encoding.UTF8.GetBytes(fileText)) }); }
public override Task <IEnumerable <FileInformation> > MigrateFileAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.FileConversion, ChildActionType); FileInformation fi = new FileInformation(FilePathHelper.RemoveDuplicateDirectories(RelativePath), File.ReadAllBytes(FullPath)); var fileList = new List <FileInformation>(); fileList.Add(fi); DoCleanUp(); LogEnd(); return(Task.FromResult((IEnumerable <FileInformation>)fileList)); }
public override async Task <IEnumerable <FileInformation> > MigrateFileAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.FileConversion, ChildActionType); // Need to have ToList call here to enumerate the collection and ensure class // converters are running before we retire this file converter task var classMigrationTasks = _classConverters.Select(converter => converter.MigrateClassAsync()).ToList(); // We want to do our cleanup now because from this point on all migration tasks // are done by class converters and we want to make sure that we retire the task // related to this file converter before we await DoCleanUp(); var allMigrationTasks = Task.WhenAll(classMigrationTasks); try { await allMigrationTasks; } // We don't provide a reference for the thrown exception here because await auto- // unwraps aggregate exceptions and throws only the first encountered exception catch { // We access allMigrationTasks.Exception instead to provide the original AggregateException var allExceptions = allMigrationTasks.Exception.Flatten().InnerExceptions; foreach (Exception e in allExceptions) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to migrate class"); } } var result = classMigrationTasks.Where(t => t.Status == TaskStatus.RanToCompletion).SelectMany(t => t.Result); LogEnd(); return(result); }
public override Task <IEnumerable <FileInformation> > MigrateFileAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.FileConversion, ChildActionType); string filename = Path.GetFileName(RelativePath); var fileList = new List <FileInformation>(); //Currently only handles web.config, package.config handled by ProjectFileConverter, others not handled if (filename.Equals(WebConfigFile, StringComparison.InvariantCultureIgnoreCase)) { //ProjectType WebForms doesn't really exist yet, but can be added for more specific configuration ConfigMigrate configMigrate = new ConfigMigrate(FullPath, ProjectType.WebForms); var migratedString = configMigrate.WebformsWebConfigMigrateHelper(); string newPath = FilePathHelper.RemoveDuplicateDirectories(Path.Combine(_relativeDirectory, Constants.AppSettingsFileName)); fileList.Add(new FileInformation(newPath, Encoding.UTF8.GetBytes(migratedString))); } DoCleanUp(); LogEnd(); return(Task.FromResult((IEnumerable <FileInformation>)fileList)); }
public override Task <IEnumerable <FileInformation> > MigrateFileAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.FileConversion, ChildActionType); // We want to add the relative path to _Host.cshtml before // prepending wwwroot/ because the web root folder is ignored // when fetching static files if (RelativePath.EndsWith(Constants.StyleSheetFileExtension, StringComparison.InvariantCultureIgnoreCase)) { _hostPageService.AddStyleSheetPath(RelativePath); } var newPath = FilePathHelper.RemoveDuplicateDirectories(Path.Combine(Constants.WebRootDirectoryName, RelativePath)); var fullPath = Path.Combine(ProjectPath, RelativePath); FileInformation fi = new FileInformation(newPath, File.ReadAllBytes(fullPath)); var fileList = new[] { fi }; DoCleanUp(); LogEnd(); return(Task.FromResult((IEnumerable <FileInformation>)fileList)); }
public override Task <IEnumerable <FileInformation> > MigrateClassAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.ClassConversion, ActionName); // NOTE: Removed temporarily until usings can be better determined, at the moment, too // many are being removed // var requiredNamespaceNames = _sourceFileSemanticModel // .GetNamespacesReferencedByType(_originalDeclarationSyntax) // .Select(namespaceSymbol => namespaceSymbol.ToDisplayString()) // // This is so we can use ComponentBase base class // .Append(Constants.BlazorComponentsNamespace); var requiredNamespaceNames = _sourceFileSemanticModel.GetOriginalUsingNamespaces().Append(Constants.BlazorComponentsNamespace); requiredNamespaceNames = CodeSyntaxHelper.RemoveFrameworkUsings(requiredNamespaceNames); var allMethods = _originalDeclarationSyntax.DescendantNodes().OfType <MethodDeclarationSyntax>(); var currentClassDeclaration = ((ClassDeclarationSyntax)_originalDeclarationSyntax) // Need to track methods so modifications can be made one after another .TrackNodes(allMethods) // Remove outdated base type references // TODO: Scan and remove specific base types in the future .ClearBaseTypes() // ComponentBase base class is required to use lifecycle events .AddBaseType(Constants.ComponentBaseClass); var orderedMethods = allMethods .Select(method => (method, LifecycleManagerService.CheckMethodPageLifecycleHook(method))) // Filter out non-lifecycle methods .Where(methodTuple => methodTuple.Item2 != null) // Order matters within new events so we order before processing .OrderBy(methodTuple => { return((int)methodTuple.Item2); }); // Remove old lifecycle methods, sort, and record their content foreach (var methodTuple in orderedMethods) { try { // This records the statements in the proper collection ProcessLifecycleEventMethod(methodTuple.Item1, (WebFormsPageLifecycleEvent)methodTuple.Item2); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to process WebForms lifecycle event method {methodTuple.Item1.Identifier} " + $"from {OriginalClassName} class at {_fullPath}"); } // Refresh node before removing var currentMethodNode = currentClassDeclaration.GetCurrentNode(methodTuple.Item1); currentClassDeclaration = currentClassDeclaration.RemoveNode(currentMethodNode, SyntaxRemoveOptions.AddElasticMarker); } // Construct new lifecycle methods and add them to the class foreach (var newLifecycleEventKvp in _newLifecycleLines) { var newLifecycleEvent = newLifecycleEventKvp.Key; var newLifecycleEventStatements = newLifecycleEventKvp.Value; try { var newMethodDeclaration = ComponentSyntaxHelper.ConstructComponentLifecycleMethod(newLifecycleEvent, newLifecycleEventStatements); currentClassDeclaration = currentClassDeclaration.AddMembers(newMethodDeclaration); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to construct new lifecycle event method for {newLifecycleEvent} Blazor event " + $"using {OriginalClassName} class at {_fullPath}"); } } // If we need to make use of the dispose method, add the IDisposable // interface to the class, usings are fine as is because this come from // the System namespace if (_newLifecycleLines.ContainsKey(BlazorComponentLifecycleEvent.Dispose)) { currentClassDeclaration = currentClassDeclaration.AddBaseType(Constants.DisposableInterface); } var namespaceNode = CodeSyntaxHelper.BuildNamespace(_originalClassSymbol.ContainingNamespace?.ToDisplayString(), currentClassDeclaration); var fileText = CodeSyntaxHelper.GetFileSyntaxAsString(namespaceNode, CodeSyntaxHelper.BuildUsingStatements(requiredNamespaceNames)); DoCleanUp(); LogEnd(); var result = new[] { new FileInformation(GetNewRelativePath(), Encoding.UTF8.GetBytes(fileText)) }; return(Task.FromResult((IEnumerable <FileInformation>)result)); }
public override Task <IEnumerable <FileInformation> > MigrateClassAsync() { LogStart(); _metricsContext.CollectActionMetrics(WebFormsActionType.ClassConversion, ActionName); var className = _originalDeclarationSyntax.Identifier.ToString(); var namespaceName = _originalClassSymbol.ContainingNamespace?.ToDisplayString(); // NOTE: Removed temporarily until usings can be better determined, at the moment, too // many are being removed //var requiredNamespaceNames = _sourceFileSemanticModel // .GetNamespacesReferencedByType(_originalDeclarationSyntax) // .Select(namespaceSymbol => namespaceSymbol.ToDisplayString()); var requiredNamespaceNames = _sourceFileSemanticModel.GetOriginalUsingNamespaces() .Union(MiddlewareSyntaxHelper.RequiredNamespaces); requiredNamespaceNames = CodeSyntaxHelper.RemoveFrameworkUsings(requiredNamespaceNames); // Make this call once now so we don't have to keep doing it later var originalDescendantNodes = _originalDeclarationSyntax.DescendantNodes(); var keepableMethods = originalDescendantNodes.OfType <MethodDeclarationSyntax>(); var processRequestMethod = keepableMethods.Where(method => LifecycleManagerService.IsProcessRequestMethod(method)).SingleOrDefault(); IEnumerable <StatementSyntax> preHandleStatements; if (processRequestMethod != null) { preHandleStatements = processRequestMethod.Body.Statements.AddComment(string.Format(Constants.CodeOriginCommentTemplate, Constants.ProcessRequestMethodName)); keepableMethods = keepableMethods.Where(method => !method.IsEquivalentTo(processRequestMethod)); _lifecycleManager.RegisterMiddlewareClass(WebFormsAppLifecycleEvent.RequestHandlerExecute, className, namespaceName, className, false); } else { preHandleStatements = new[] { CodeSyntaxHelper.GetBlankLine().AddComment(string.Format(Constants.IdentificationFailureCommentTemplate, ProcessRequestDiscovery, InvokePopulationOperation)) }; } // We have completed any possible registration by this point _lifecycleManager.NotifyMiddlewareSourceProcessed(); var fileText = string.Empty; try { var middlewareClassDeclaration = MiddlewareSyntaxHelper.ConstructMiddlewareClass( middlewareClassName: className, shouldContinueAfterInvoke: false, constructorAdditionalStatements: originalDescendantNodes.OfType <ConstructorDeclarationSyntax>().FirstOrDefault()?.Body?.Statements, preHandleStatements: preHandleStatements, additionalFieldDeclarations: originalDescendantNodes.OfType <FieldDeclarationSyntax>(), additionalPropertyDeclarations: originalDescendantNodes.OfType <PropertyDeclarationSyntax>(), additionalMethodDeclarations: keepableMethods); var namespaceNode = CodeSyntaxHelper.BuildNamespace(namespaceName, middlewareClassDeclaration); fileText = CodeSyntaxHelper.GetFileSyntaxAsString(namespaceNode, CodeSyntaxHelper.BuildUsingStatements(requiredNamespaceNames)); } catch (Exception e) { LogHelper.LogError(e, $"{Rules.Config.Constants.WebFormsErrorTag}Failed to construct new HttpHandler file content from {OriginalClassName} class at {_fullPath}"); } DoCleanUp(); LogEnd(); // Http modules are turned into middleware and so we use a new middleware directory var newRelativePath = FilePathHelper.RemoveDuplicateDirectories(Path.Combine(Constants.MiddlewareDirectoryName, FilePathHelper.AlterFileName(_relativePath, newFileName: className))); // TODO: Potentially remove certain folders from beginning of relative path var result = new[] { new FileInformation(newRelativePath, Encoding.UTF8.GetBytes(fileText)) }; return(Task.FromResult((IEnumerable <FileInformation>)result)); }