From 919f8c558afafbbf33fc500909dd283d259659dc Mon Sep 17 00:00:00 2001 From: manpatel3107 <44624626+manpatel3107@users.noreply.github.com> Date: Wed, 9 Jan 2019 15:38:50 -0800 Subject: [PATCH] Handling file deletion and on change events on fil (#19) * Resolving the security vulnerability * Added the changes for handling on change and delete events on .ts and .html files. --- package.json | 2 +- src/constants/extension-constants.ts | 13 +- src/execute-mezzurite-framework-rules.ts | 6 +- src/extension.ts | 50 +++++-- src/frameworks/mezzurite-angular.ts | 56 +------ src/mezzurite-extension-main.ts | 177 ++++++++++++++++++++++- src/treeview/components-treeview.ts | 14 +- src/treeview/modules-treeview.ts | 22 ++- src/utils/mezzurite-utils.ts | 51 +++++++ 9 files changed, 315 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index dee4998..6c98f49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mezzurite", - "displayName": "Microsoft", + "displayName": "Mezzurite", "description": "Instrument your application with Mezzurite SDK quickly and easily with this extension.", "version": "2.0.0", "publisher": "mezzurite-devs", diff --git a/src/constants/extension-constants.ts b/src/constants/extension-constants.ts index 4fe2ecc..0e3e5e9 100644 --- a/src/constants/extension-constants.ts +++ b/src/constants/extension-constants.ts @@ -6,6 +6,7 @@ const ExtensionConstants = { pathForAppsPackageJson : "**/package.json", pathForNodeModules : "**/node_modules/**", pathForTypescriptFiles : "**/*.ts", + pathForHtmlFiles : "**/*.html", titleForWelcomePage : "Mezzurite Extension", placeholderForStyleTag : "##StyleLink##", webviewStyleName : "style.css", @@ -39,18 +40,26 @@ const ExtensionConstants = { mezzuriteAngularJs:"@microsoft/mezzurite-angularjs", mezzuriteAngular:"@microsoft/mezzurite-angular", mezzuriteReact:"@microsoft/mezzurite-react", + + componentsNotFound: "No components found!", + modulesNotFound: "No modules found!", + notFoundId:"NotFound", + githubRepo:"Click here to visit out Github Repository!", + mezzuGithubUrl:"https://github.com/Microsoft/Mezzurite" + } const CommandConstants = { validateMezzuriteCommand : "extension.validateMezzurite", displayLandingPageCommand : "extension.displayLandingPage", trackComponentCommand: "componentTreeView.selectNode", - trackModuleCommand:"moduleTreeView.selectNode" + trackModuleCommand:"moduleTreeView.selectNode", + openUrl:"vscode.open" } const TreeviewTitleConstants = { markedComponent:"This component is marked for performance tracking.", - unmarkedComponent:"Missing 'mezzurite' directive. Missing 'component-title' directive.", + unmarkedComponent:"Missing 'mezzurite' and 'component-title' directive.", markedModule:"This module is marked for performance tracking.", missingMezzuImport:"Missing mezzurite import declaration.", missingMezzuForRoot:"Missing AngularPerfModule.forRoot() statement.", diff --git a/src/execute-mezzurite-framework-rules.ts b/src/execute-mezzurite-framework-rules.ts index 5fa0d7f..bc1bdf7 100644 --- a/src/execute-mezzurite-framework-rules.ts +++ b/src/execute-mezzurite-framework-rules.ts @@ -1,5 +1,6 @@ import {ExtensionConstants} from './constants/extension-constants'; import {MezzuriteAngularV1} from './frameworks/mezzurite-angular'; +import {MezzuriteUtils} from './utils/mezzurite-utils'; export class ExecuteMezzuriteFrameworkRules { private frameworkName: string; @@ -24,7 +25,10 @@ export class ExecuteMezzuriteFrameworkRules { break; default: console.log('Mezzurite framework name '+ this.frameworkName + ' or version '+ this.frameworkversion +' is invalid!'); } - return await mezzuriteFramework.executeFrameworkSpecificRules(); + if(mezzuriteFramework){ + return await mezzuriteFramework.executeFrameworkSpecificRules(); + } + return MezzuriteUtils.createOutputObject([], []); } } diff --git a/src/extension.ts b/src/extension.ts index 8ead26e..8430a2f 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,6 +11,10 @@ import {MezzuriteExtension} from './mezzurite-extension-main'; export async function activate(context: vscode.ExtensionContext) { // create a new Mezzurite extension let mezzuriteExtension = new MezzuriteExtension(); + + // Create file system watcher for .ts and .html files + let tsFileSystemWatcher = vscode.workspace.createFileSystemWatcher(ExtensionConstants.pathForTypescriptFiles, false, true, false); + let htmlFileSystemWatcher = vscode.workspace.createFileSystemWatcher(ExtensionConstants.pathForHtmlFiles, false, true, false); // Check if mezzurite dependency is found or not var mezzuriteDependency: any = await mezzuriteExtension.verifyMezzurite(); @@ -25,6 +29,7 @@ export async function activate(context: vscode.ExtensionContext) { let validateMezzurite = commands.registerCommand( CommandConstants.validateMezzuriteCommand, async () => { let updatedResults = await mezzuriteExtension.executeMezzuriteRules(mezzuriteDependency, undefined); mezzuriteExtension.displayComponentsTreeView(updatedResults, context); + mezzuriteExtension.displayModulesTreeView(updatedResults, context); }); // Display components treeview @@ -33,17 +38,25 @@ export async function activate(context: vscode.ExtensionContext) { // Display modules treeview mezzuriteExtension.displayModulesTreeView(ruleResults, context); + // Command for On Save of a .ts or .html file vscode.workspace.onDidSaveTextDocument(async (document) => { if(document.languageId === ExtensionConstants.typescript){ - let updatedResults = await mezzuriteExtension.executeMezzuriteRules(mezzuriteDependency, document.fileName); - updatedResults = mezzuriteExtension.mergeResultsAfterSave(ruleResults, updatedResults); - mezzuriteExtension.displayComponentsTreeView(updatedResults, context); + ruleResults = await mezzuriteExtension.onEditOrCreateTsFile(ruleResults, mezzuriteDependency, document.fileName, context); } else if(document.languageId === ExtensionConstants.html){ - // @TODO: This is in progress - // Check if that html file is present inside oldResults.listOfComponents and verifying the 'templateUrl' property + ruleResults = await mezzuriteExtension.onEditOrCreateHtmlFile(ruleResults, document.fileName, context); } - }); + }); + + // Command for On Delete of a .ts file + tsFileSystemWatcher.onDidDelete(async function(e){ + ruleResults = await mezzuriteExtension.onDeleteTsFile(ruleResults, e.fsPath, context); + }); + + // Command for On Delete of a .html file + htmlFileSystemWatcher.onDidDelete(async function(e){ + ruleResults = await mezzuriteExtension.onDeleteHtmlFile(ruleResults, e.fsPath, context); + }); // Command for displaying the landing page let landingPageCommand = commands.registerCommand( CommandConstants.displayLandingPageCommand, () => { @@ -52,19 +65,28 @@ export async function activate(context: vscode.ExtensionContext) { // Command for selecting a node in components tree view let componentsTreeView = commands.registerCommand(CommandConstants.trackComponentCommand, (item: any) => { - vscode.workspace.openTextDocument(item.path).then(doc => { - vscode.window.showTextDocument(doc); - }); + if(item.path === ExtensionConstants.notFoundId){ + vscode.commands.executeCommand(CommandConstants.openUrl, vscode.Uri.parse(ExtensionConstants.mezzuGithubUrl)) + } + else{ + vscode.workspace.openTextDocument(item.path).then(doc => { + vscode.window.showTextDocument(doc); + }); + } }); - // Command for selecting a node in components tree view + // Command for selecting a node in modules tree view let modulesTreeView = commands.registerCommand(CommandConstants.trackModuleCommand, (item: any) => { - vscode.workspace.openTextDocument(item.path).then(doc => { - vscode.window.showTextDocument(doc); - }); + if(item.path === ExtensionConstants.notFoundId){ + vscode.commands.executeCommand(CommandConstants.openUrl, vscode.Uri.parse(ExtensionConstants.mezzuGithubUrl)) + } + else{ + vscode.workspace.openTextDocument(item.path).then(doc => { + vscode.window.showTextDocument(doc); + }); + } }); - context.subscriptions.push(validateMezzurite); context.subscriptions.push(landingPageCommand); context.subscriptions.push(componentsTreeView); diff --git a/src/frameworks/mezzurite-angular.ts b/src/frameworks/mezzurite-angular.ts index d8a2320..24cad0a 100644 --- a/src/frameworks/mezzurite-angular.ts +++ b/src/frameworks/mezzurite-angular.ts @@ -25,7 +25,7 @@ export class MezzuriteAngularV1{ files = await MezzuriteUtils.searchWorkspace(workspace, ExtensionConstants.pathForTypescriptFiles, ExtensionConstants.pathForNodeModules); } else{ - var editedFilePath = "**/" + MezzuriteAngularV1.getFileNameFromPath(this.filePath); + var editedFilePath = "**/" + MezzuriteUtils.getFileNameFromPath(this.filePath); files = await MezzuriteUtils.searchWorkspace(workspace, editedFilePath, ExtensionConstants.pathForNodeModules); } let data: string; @@ -46,14 +46,7 @@ export class MezzuriteAngularV1{ listOfComponents = await MezzuriteAngularV1.getListOfComponents(filePath, data, listOfComponents); } } - return MezzuriteAngularV1.outputObject(listOfComponents, listOfModules); - } - - static outputObject(listOfComponents: any, listOfModules: any){ - var outputObj: any = {}; - outputObj["listOfComponents"] = listOfComponents; - outputObj["listOfModules"] = listOfModules; - return outputObj; + return MezzuriteUtils.createOutputObject(listOfComponents, listOfModules); } /** @@ -302,7 +295,7 @@ export class MezzuriteAngularV1{ // Look for 'template' and 'templateUrl' properties in decorator object var templateUrlProperty = MezzuriteAngularV1.getDecoratorProperty(decorator, ExtensionConstants.templateUrl); if(templateUrlProperty !== undefined && templateUrlProperty !== ""){ - MezzuriteAngularV1.checkForTemplateUrlProperty(templateUrlProperty, componentObject); + await MezzuriteAngularV1.checkForTemplateUrlProperty(templateUrlProperty, componentObject); } var templateProperty = MezzuriteAngularV1.getDecoratorProperty(decorator, ExtensionConstants.template); if(templateProperty !== undefined && templateProperty !== ""){ @@ -320,8 +313,8 @@ export class MezzuriteAngularV1{ var templateUrlValue = templateUrlProperty.getInitializer().compilerNode.text; if(templateUrlValue !== ""){ componentObject.templateUrl = templateUrlValue; - var fileName: string = MezzuriteAngularV1.getFileNameFromPath(templateUrlValue); - var found = await MezzuriteAngularV1.parseExternalHTMLFile(fileName); + var fileName: string = MezzuriteUtils.getFileNameFromPath(templateUrlValue); + var found = await MezzuriteUtils.parseExternalHTMLFile(fileName, workspace); if(found){ componentObject.status = ExtensionConstants.marked; } @@ -337,7 +330,7 @@ export class MezzuriteAngularV1{ var templateValue = templateProperty.getInitializer().compilerNode.text; if(templateValue !== ""){ componentObject.template = ExtensionConstants.htmlTemplateProvided; - if(MezzuriteAngularV1.verifyComponentMarking(templateValue)){ + if(MezzuriteUtils.verifyComponentMarking(templateValue)){ componentObject.status = ExtensionConstants.marked; } } @@ -373,40 +366,5 @@ export class MezzuriteAngularV1{ return moduleObject; } - /** - * This method is used to get the html file name from file path - * @param filePath of the html template - * @return heml file name - */ - static getFileNameFromPath(filePath: string){ - var lastIndex = filePath.lastIndexOf('/') > -1? filePath.lastIndexOf('/') : filePath.lastIndexOf('\\'); - return filePath.substring(lastIndex + 1, filePath.length); - } - - /** - * This method is used to parse the html template for the components with templateUrl property - * @param filePath of the html template - * @return true, if parsed html is marked for tracking, otherwise, false - */ - static async parseExternalHTMLFile(fileName: string){ - var templateString: any; - // Get the html file and parse its contents for mezzurite markings - let files: any = await MezzuriteUtils.searchWorkspace(workspace, "**/" + fileName, ExtensionConstants.pathForNodeModules); - if(files[0] && files[0].fsPath){ - templateString = MezzuriteUtils.readFileFromWorkspace(files[0].fsPath, 'utf8'); - } - return MezzuriteAngularV1.verifyComponentMarking(templateString); - } - - /** - * This method verifies whether component is marked or not - * @param htmlString is the html template of the component - * @return true, if parsed html contains the mezzurite directive and component-title, otherwise, false - */ - static verifyComponentMarking(htmlString: string){ - if(htmlString && htmlString.indexOf(ExtensionConstants.mezzuriteDirective) > -1 && htmlString.indexOf(ExtensionConstants.componentTitleDirective)> -1){ - return true; - } - return false; - } + } \ No newline at end of file diff --git a/src/mezzurite-extension-main.ts b/src/mezzurite-extension-main.ts index 9f06994..514abe2 100644 --- a/src/mezzurite-extension-main.ts +++ b/src/mezzurite-extension-main.ts @@ -1,9 +1,11 @@ import * as vscode from 'vscode'; +import {workspace} from 'vscode'; import {LandingPage} from './landing-page'; import {MezzuriteDependency} from './verify-mezzurite-dependency'; import {ExecuteMezzuriteFrameworkRules} from './execute-mezzurite-framework-rules'; import {ComponentsProvider} from './treeview/components-treeview' -import {ModulesProvider} from './treeview/modules-treeview' +import {ModulesProvider} from './treeview/modules-treeview'; +import {MezzuriteUtils} from './utils/mezzurite-utils'; import { ExtensionConstants } from './constants/extension-constants'; /** * Mezzurite extension class @@ -46,6 +48,66 @@ export class MezzuriteExtension{ } } + /** + * This method updates the output results whenever a typescript file is created or edited and then saved + * @param list of old modules or components output data + * @param mezzurite dependency object + * @param filepath of changed file + * @param context which is editor context needed to display the output + * @return returns array of modules or components + */ + public async onEditOrCreateTsFile(ruleResults: any, mezzuriteDependency: any, filePath: string, context: any){ + let updatedResults = await this.executeMezzuriteRules(mezzuriteDependency, filePath); + updatedResults = this.mergeResultsAfterSave(ruleResults, updatedResults); + this.displayComponentsTreeView(updatedResults, context); + this.displayModulesTreeView(updatedResults, context); + return updatedResults; + } + + /** + * This method updates the output results whenever a typescript file is deleted + * @param list of old modules or components output data + * @param filepath of changed file + * @param context which is editor context needed to display the output + * @return returns array of modules or components + */ + public async onDeleteTsFile(ruleResults: any, filePath: string, context: any){ + var updatedResults: any = {}; + updatedResults["listOfComponents"] = MezzuriteExtension.removeComponentOnDelete(ruleResults.listOfComponents, filePath); + updatedResults["listOfModules"] = MezzuriteExtension.removeModuleOnDelete(ruleResults.listOfModules, filePath); + this.displayComponentsTreeView(updatedResults, context); + this.displayModulesTreeView(updatedResults, context); + return updatedResults; + } + + /** + * This method updates the output results whenever a html file is created or edited and then saved + * @param list of old modules or components output data + * @param filepath of changed file + * @param context which is editor context needed to display the output + * @return returns array of modules or components + */ + public async onEditOrCreateHtmlFile(ruleResults: any, filePath: string, context: any){ + let updatedResults = await this.verifyHtmlComponent(ruleResults, filePath); + this.displayComponentsTreeView(updatedResults, context); + return updatedResults; + } + + /** + * This method updates the output results whenever a html file is deleted + * @param list of old modules or components output data + * @param filepath of changed file + * @param context which is editor context needed to display the output + * @return returns array of modules or components + */ + public async onDeleteHtmlFile(ruleResults: any, filePath: string, context: any){ + var updatedResults: any = {}; + updatedResults["listOfComponents"] = MezzuriteExtension.removeHtmlComponentOnDelete(ruleResults.listOfComponents, filePath); + updatedResults["listOfModules"] = ruleResults.listOfModules; + this.displayComponentsTreeView(updatedResults, context); + return updatedResults; + } + /** * This method creates tree view for all the components present in the workspace * @param results object consisting of all the components @@ -64,12 +126,123 @@ export class MezzuriteExtension{ vscode.window.registerTreeDataProvider(ExtensionConstants.modulesTreeview, treeView); } + /** + * This method merge the old output data with a newly saved or created module or component data + * @param list of old modules or components output data + * @param list of newly saved or created modules or component + * @return returns merged array of modules or components + */ public mergeResultsAfterSave(oldResults: any, newResults: any){ + var updatedResults; + // Merge components + if(newResults.listOfComponents.length > 0){ + updatedResults = MezzuriteExtension.mergeComponentsAfterSave(oldResults, newResults); + } + // Merge Modules + if(newResults.listOfModules.length > 0){ + updatedResults = MezzuriteExtension.mergeModulesAfterSave(oldResults,newResults); + } + return updatedResults; + } + + /** + * This method merge the old components output data with a newly saved or created component data + * @param list of old components output data + * @param list of newly saved or created component + * @return returns merged array of components to be displayed + */ + public static mergeComponentsAfterSave(oldResults: any, newResults: any){ + var found = false; for(var i=0;i -1){ + var found = await MezzuriteUtils.parseExternalHTMLFile(fileName, workspace); + if(found){ + oldResults.listOfComponents[i].status = ExtensionConstants.marked; + } + else{ + oldResults.listOfComponents[i].status = ExtensionConstants.unmarked; + } + return oldResults; + } + } + } } diff --git a/src/treeview/components-treeview.ts b/src/treeview/components-treeview.ts index 03bd138..c079da4 100644 --- a/src/treeview/components-treeview.ts +++ b/src/treeview/components-treeview.ts @@ -61,7 +61,17 @@ export class ComponentsProvider implements vscode.TreeDataProvider { private getTitleForUnmarkedModules(moduleObject: any){ var title = ""; - if(moduleObject.importStmt){ + if(!moduleObject.importStmt){ title = title + TreeviewTitleConstants.missingMezzuImport; } - if(moduleObject.forRoot){ + if(!moduleObject.forRoot){ title = title + TreeviewTitleConstants.missingMezzuForRoot; } - if(moduleObject.routerStart){ + if(!moduleObject.routerStart){ title = title + TreeviewTitleConstants.missingMezzuStart; } return title; @@ -77,7 +77,19 @@ export class ModulesProvider implements vscode.TreeDataProvider { } return moduleItems; - } + } + else{ + var message = new ModuleItem(ExtensionConstants.modulesNotFound, ExtensionConstants.notFoundId, "", ExtensionConstants.githubRepo, + vscode.TreeItemCollapsibleState.None); + message.command = { + command: CommandConstants.trackModuleCommand, + title: '', + arguments: [message] + }; + + moduleItems.push(message); + return moduleItems; + } return []; } @@ -101,7 +113,7 @@ export class ModuleItem extends vscode.TreeItem { } get description(): string { - return this.label; + return ""; } get pathName(): string { diff --git a/src/utils/mezzurite-utils.ts b/src/utils/mezzurite-utils.ts index ca0b5ba..9c8ca91 100644 --- a/src/utils/mezzurite-utils.ts +++ b/src/utils/mezzurite-utils.ts @@ -1,4 +1,5 @@ import * as fs from "fs"; +import {ExtensionConstants} from '../constants/extension-constants'; export class MezzuriteUtils { @@ -42,4 +43,54 @@ export class MezzuriteUtils { return data; } + /** + * This method verifies whether component is marked or not + * @param htmlString is the html template of the component + * @return true, if parsed html contains the mezzurite directive and component-title, otherwise, false + */ + static verifyComponentMarking(htmlString: string){ + if(htmlString && htmlString.indexOf(ExtensionConstants.mezzuriteDirective) > -1 && htmlString.indexOf(ExtensionConstants.componentTitleDirective)> -1){ + return true; + } + return false; + } + + /** + * This method is used to get the html file name from file path + * @param filePath of the html template + * @return heml file name + */ + static getFileNameFromPath(filePath: string){ + var lastIndex = filePath.lastIndexOf('/') > -1? filePath.lastIndexOf('/') : filePath.lastIndexOf('\\'); + return filePath.substring(lastIndex + 1, filePath.length); + } + + /** + * This method is used to parse the html template for the components with templateUrl property + * @param filePath of the html template + * @return true, if parsed html is marked for tracking, otherwise, false + */ + static async parseExternalHTMLFile(fileName: string, workspace: any){ + var templateString: any; + // Get the html file and parse its contents for mezzurite markings + let files: any = await MezzuriteUtils.searchWorkspace(workspace, "**/" + fileName, ExtensionConstants.pathForNodeModules); + if(files[0] && files[0].fsPath){ + templateString = MezzuriteUtils.readFileFromWorkspace(files[0].fsPath, 'utf8'); + } + return MezzuriteUtils.verifyComponentMarking(templateString); + } + + /** + * This method creates an output object to be displayed as results + * @param list of components + * @param list of modules + * @return output object + */ + static createOutputObject(listOfComponents: any, listOfModules: any){ + var outputObj: any = {}; + outputObj["listOfComponents"] = listOfComponents; + outputObj["listOfModules"] = listOfModules; + return outputObj; + } + } \ No newline at end of file