/// <summary>
        /// Confirms the target project folder exists in the project, if not will create it.
        /// </summary>
        /// <param name="projectFolder">The source project folder to check.</param>
        /// <param name="folderName">The name of the folder to create or return if it exists.</param>
        /// <returns>The target project folder.</returns>
        public static async Task <VsProjectFolder> CheckAddFolder(this VsProjectFolder projectFolder, string folderName)
        {
            //Call CodeFactory API and get the children of the project folder.
            var projectFolders = await projectFolder.GetChildrenAsync(false);

            //Search for the project folder to confirm it exists, if not create it and return the created folder.
            return(projectFolders.Where(m => m.Name.Equals(folderName)).Cast <VsProjectFolder>().FirstOrDefault() ??
                   await projectFolder.AddProjectFolderAsync(folderName));
        }
Example #2
0
        /// <summary>
        /// Migrates logic class files over to the blazor server project.
        /// </summary>
        /// <param name="webFormProjectData">List of pre cached models for from the web form project.</param>
        /// <param name="webFormProject">The web forms project that we are migrating data from.</param>
        /// <param name="blazorServerProject">The blazor server project this is being migrated to.</param>
        public async Task MigrateLogic(IReadOnlyList <VsModel> webFormProjectData, VsProject webFormProject, VsProject blazorServerProject)
        {
            try
            {
                //Informing the dialog the migration step has started.
                await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Running);

                var childFiles = webFormProjectData.GetSourceCodeDocumentsAsync(true);

                //we don't want any known aspx/ascx files hitching a ride.  just plain vanilla *.cs files should qualify.
                var logicFiles = childFiles.Where(p => (!p.Name.ToLower().Contains("aspx.") && !p.Name.ToLower().Contains("ascx.") && !p.Name.ToLower().Contains("asax."))).ToList();

                //put logic files (using the file system into the target under the project root)
                foreach (VsCSharpSource sourceDocument in logicFiles)
                {
                    //look for specific files that are native to a WebForm app and skip them. ** TODO: move this to a config setting maybe?
                    if (sourceDocument.Name.ToLower().Contains("bundleconfig"))
                    {
                        continue;
                    }
                    if (sourceDocument.Name.ToLower().Contains("assemblyinfo"))
                    {
                        continue;
                    }
                    if (sourceDocument.Name.ToLower().Contains("startup"))
                    {
                        continue;
                    }
                    if (sourceDocument.Name.ToLower().Contains(".master"))
                    {
                        continue;
                    }

                    var logicDocument = await sourceDocument.LoadDocumentModelAsync();

                    var parentFolders = await logicDocument.GetParentFolders();

                    var source  = sourceDocument.SourceCode;
                    var docText = await logicDocument.GetDocumentContentAsStringAsync();

                    if (parentFolders.Count >= 1)
                    {
                        parentFolders.Reverse(); //The folders are returned in leaf-to-trunk so need to reverse the order for the next step.
                        VsProjectFolder createdFolder = null;

                        //deal with source folder hierarchy
                        for (int i = 0; i < parentFolders.Count; i++)
                        {
                            if (i > 0)
                            {
                                createdFolder = await createdFolder.CheckAddFolder(parentFolders[i].Name);
                            }
                            else
                            {
                                createdFolder = await blazorServerProject.CheckAddFolder(parentFolders[i].Name);
                            }
                        }

                        //copy the file.  We only really care about the most leaf/edge subfolder so its safe to use the creatdFolder variable here.
                        docText = docText.Replace(source.Classes.First().Namespace, $"{blazorServerProject.Name}.{createdFolder.Name}");

                        var targetFolderFiles = await createdFolder.GetChildrenAsync(false);

                        if (!targetFolderFiles.Any(c => c.Name.ToLower().Equals(logicDocument.Name.ToLower())))
                        {
                            await createdFolder.AddDocumentAsync(logicDocument.Name, docText);

                            //Updating the dialog with a status
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Information,
                                                                           $"Copied logic file: {logicDocument.Name} to project {blazorServerProject.Name} location: {Path.Combine(createdFolder.Path, logicDocument.Name)}");
                        }
                        else
                        {
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Warning,
                                                                           $"Logic file: {logicDocument.Name} already exists in target folder location: {Path.Combine(createdFolder.Path, logicDocument.Name)} and was skipped.");
                        }
                    }
                    else
                    {
                        var projFiles = await blazorServerProject.GetChildrenAsync(false);

                        if (!projFiles.Any(c => c.Name.ToLower().Equals(logicDocument.Name.ToLower())))
                        {
                            docText = docText.Replace(source.Classes.First().Namespace, $"{blazorServerProject.Name}");

                            var thing = await blazorServerProject.AddDocumentAsync(logicDocument.Name, docText);

                            //Updating the dialog with a status
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Information,
                                                                           $"Copied static file: {logicDocument.Name} to project {blazorServerProject.Name} location: {Path.Combine(blazorServerProject.Path, logicDocument.Name)}");
                        }
                        else
                        {
                            //Updating the dialog with a status
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Warning,
                                                                           $"Static file: {logicDocument.Name} already exists in project {blazorServerProject.Name} location: {Path.Combine(blazorServerProject.Path, logicDocument.Name)} and was skipped.");
                        }
                    }
                }

                //Completed the migration step informing the dialog.
                await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Passed);
            }
            catch (Exception unhandledError)
            {
                //Dumping the exception that occured directly into the status so the user can see what happened.
                await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AppLogic, MessageTypeEnum.Error, $"The following unhandled error occured. '{unhandledError.Message}'");

                //Updating the status that the step failed
                await _statusTracking.UpdateStepStatusAsync(MigrationStepEnum.AppLogic, MigrationStatusEnum.Failed);
            }
        }
        /// <summary>
        /// Helper method that converts Aspx Pages to Blazor pages.
        /// </summary>
        /// <param name="sourcePage">The source aspx page to be converted.</param>
        /// <param name="targetProject">The target blazor project to write to.</param>
        /// <param name="targetPagesFolder">The target visual studio project folder the converted aspx will be added to.</param>
        /// <param name="sourceCodeBehind">Optional parameter that provides the code behind file for the aspx page to be converted also.</param>
        /// <returns>Flag used to determine if the conversion was successful.</returns>
        private async Task ConvertAspxPage(VsDocument sourcePage, VsProject targetProject, VsProjectFolder targetPagesFolder, VsCSharpSource sourceCodeBehind = null, VsDocument sourceDocCodeBehind = null)
        {
            try
            {
                //Getting the content from the source document.
                var pageContent = await sourcePage.GetDocumentContentAsStringAsync(); //File.ReadAllText(result.Path);

                //grab the <%Page element from the source and pull it from the text.  Its meta data anyway and just screws up the conversion down the line.
                var pageHeaderData = System.Text.RegularExpressions.Regex.Match(pageContent, @"<%@\s*[^%>]*(.*?)\s*%>").Value;

                if (pageHeaderData.Length > 0)
                {
                    pageContent = Regex.Replace(pageContent, @"<%@\s*[^%>]*(.*?)\s*%>", string.Empty);
                }

                //Swap ASP.NET string tokens for Razor syntax  (<%, <%@, <%:, <%#:, etc
                var targetText = RemoveASPNETSyntax(pageContent);

                //Convert ASP.NET into Razor syntax.  **This actually presumes that any controls like <asp:ListView..
                //will have an equivalent Razor component in place called <ListView.. etc
                var conversionData = await ReplaceAspControls(targetText);

                //Drop the pageHeaderData into the Dictionary for later processing by any downstream T4 factories
                conversionData.Add("HeaderData", pageHeaderData);

                //Getting the source code from the code behind file provided.
                var codeSource = sourceCodeBehind?.SourceCode;
                if ((codeSource == null) && (sourceDocCodeBehind != null))
                {
                    codeSource = await sourceDocCodeBehind.GetCSharpSourceModelAsync();
                }

                //put the files in the target project
                String targetFileName = Path.GetFileNameWithoutExtension(sourcePage.Path);
                conversionData.Add("Namespace", $"{targetProject.DefaultNamespace}.Pages");

                //Setup Page directives, using statements etc
                var targetWithMeta = await SetRazorPageDirectives(targetFileName, conversionData);

                //Adding the converted content from the aspx page to the new razor page.
                VsDocument success = await targetPagesFolder.AddDocumentAsync($"{targetFileName}.razor", targetWithMeta);

                if (success != null)
                {
                    //Updating the dialog with the status the aspx page has been converted.
                    await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages,
                                                                   MessageTypeEnum.Information,
                                                                   $"Converted the aspx page to a razor page, added razor page {targetFileName}.razor");

                    //If we have the source code from the original aspx page add it to the razor pages folder.
                    if (codeSource != null)
                    {
                        //Creating a CodeFactory model store this will be used to pass data to a T4 factory.
                        CsModelStore modelStore = new CsModelStore();

                        //Adding the current class from the code behind into the model store for processing.
                        modelStore.SetModel(codeSource.Classes.FirstOrDefault());

                        //Processing the T4 factory and loading the source code.
                        var codeBehindFormattedSourceCode =
                            Templates.PageCodeBehind.GenerateSource(modelStore, conversionData);

                        //Calling the CodeFactory project system and adding a new code behind file and injecting the formatted code into the file.
                        var codeBehind = await targetPagesFolder.AddDocumentAsync($"{targetFileName}.razor.cs", codeBehindFormattedSourceCode);

                        if (codeBehind != null)
                        {
                            //Updating the dialog with the status the aspx page code behind has been converted.
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages,
                                                                           MessageTypeEnum.Information,
                                                                           $"Converted the aspx page code behind file to a razor page code behind file, added code behind file {targetFileName}.razor.cs");
                        }
                        else
                        {
                            //Updating the dialog with the status the aspx page code behind failed
                            await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages,
                                                                           MessageTypeEnum.Error,
                                                                           $"Failed the conversion of the aspx page code behind file to a razor page code behind file {targetFileName}.razor.cs");
                        }
                    }
                }
                else
                {
                    //Updating the dialog with the status the aspx page failed conversion.
                    await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages,
                                                                   MessageTypeEnum.Error,
                                                                   $"Failed the conversion of the aspx page {targetFileName}.razor. Will not convert the code behind file.");
                }
            }
            catch (Exception unhandledError)
            {
                await _statusTracking.UpdateCurrentStatusAsync(MigrationStepEnum.AspxPages, MessageTypeEnum.Error,
                                                               $"The following unhandled error occured while trying to convert the aspx page. '{unhandledError.Message}'");
            }
        }