Click here to Skip to main content
Click here to Skip to main content

Tagged as

Redundant References Remover VSX

, 29 Jul 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
A Visual Studio Extension that locates and removes duplicate script references in a web project.
 


Introduction 

A Visual Studio Extension that locates and removes duplicate script references in a web project.

Problem Statement 

Whilst developing a web project, you will add some script references (JavaScript\CSS) to your page (well, duh).

But then you forget which scripts you added, and add them again.
(happens a lot with MVC 3 where you don't have a visual designer and the page can become quite big)

so basically you end up with something like this:



Referencing a script twice will not only slow down the website, it may also lead to an unexpected behavior as a JavaScript function calling a controller can be executed twice.

Sometimes the scenario gets more complicated, when a reference to the same script is included in both the layout/master page-file as well as a partial view.


Solution

Redundant References Remover Extension solves these problems, it scans the views in the web-project looking for duplicated references and removes them.

Downloads

Download Extension
Source Code

After the installation, the tool appears under Other Windows

Code Anatomy 

There are 3 services that do the work

IVisualStudioService handles Visual Studio Interaction
 public interface IVisualStudioService
    {
        WebProject GetWebProject();
 
        IList<Page> GetPages(ProjectItems item);
 
        vsSaveStatus RemoveReference(ProjectItem projectItem, string reference, int offset);
    } 
IPagesService handles processing of pages and scanning for repeated references
public interface IPagesService
    {
        void ProcessPages(IEnumerable<Page> pagesList);
 
        IEnumerable<Page> GetDuplicatesInSameFile(IEnumerable<Page> pages);
 
        IEnumerable<ParentChildDuplicates> GetDuplicatesInLayoutAndFile(IEnumerable<Page> pages);
    }  
IRegexService Extracts different parts of info from the Page's html
  public interface IRegexService
    {
        IEnumerable<string> GetReferences(string pageContent);
 
        string GetDefaultLayoutPageName(string content);
 
        IEnumerable<string> GetMultipleLayouts(string content);
 
        IEnumerable<string> GetPartialViews(string content);
 
        IEnumerable<string> GetWebControls(string content);
 
        bool CheckPageHasNullLayout(string content);
 
        string GetMasterPageName(string content);
    }  
There are 3 types of web projects, mvc2, mvc3 and web forms, each handle the master\child layout differently.

So we make the parent class WebFormPagesService with virtual functions.
namespace Services.Implementation
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Entities;
    using Services.Contracts;
    using EnvDTE;
 
    public class WebFormPagesService : IPagesService
    {
        #region Member Variables
 
        protected readonly IRegexService _regexService;
 
        #endregion Member Variables
 
        #region Constructor
 
        public WebFormPagesService(IRegexService regexService)
        {
            this._regexService = regexService;
        }
 
        #endregion Constructor
 
        #region IPagesService
 
        void IPagesService.ProcessPages(IEnumerable<Page> pagesList)
        {
            foreach (Page page in pagesList)
            {
                page.References = GetReferences(page);
                var parents = GetParents(page.Name, page.Content, pagesList);
                if (page.Parents != null)
                {
                    page.Parents.Concat(parents);
                }
                else
                {
                    page.Parents = parents;
                }
                var children = GetChildren(page.Content, pagesList);
                foreach (var child in children)
                {
                    if (child.Parents == null)
                    {
                        child.Parents = new List<Page>();
                    }
 
                    child.Parents.Add(page);
                }
            }
 
            LinkParents(pagesList);
        }
 
        IEnumerable<Page> IPagesService.GetDuplicatesInSameFile(IEnumerable<Page> pages)
        {
            foreach (var page in pages)
            {
                var duplicates = page.References.Where(p => p.Value != 1).ToDictionary(p => p.Key, p => p.Value);
 
                if (duplicates.Count != 0)
                {
                    yield return new Page
                    {
                        Content = page.Content,
                        Item = page.Item,
                        Name = page.Name,
                        Parents = page.Parents,
                        References = duplicates
                    };
                }
            }
        }
 
        IEnumerable<ParentChildDuplicates> IPagesService.GetDuplicatesInLayoutAndFile(IEnumerable<Page> pages)
        {
            var duplicatesList = new List<ParentChildDuplicates>();
 
            foreach (var child in pages)
            {
                var childReferences = child.References.Keys;
 
                foreach (var parent in child.Parents)
                {
                    var parentsReferences = parent.References.Keys;
                    var commonReferences = parentsReferences.Intersect(childReferences);
 
                    if (commonReferences.Count() > 0)
                    {
                        var parentChildDuplicate = new ParentChildDuplicates { Parent = parent, Child = child, Duplicates = commonReferences };
                        duplicatesList.Add(parentChildDuplicate);
                    }
                }
            }
 
            return duplicatesList;
        }
 
        #endregion
 
        .
 
        .
 
        .
    }
}
  

Conclusion

That's it, the code is pretty much straight forward.
If you have any comment\suggestion\question about the implementation\code topology drop me a line Wink | ;)

Improvement\Future Work

Currently the code doesn't support detecting the Inclusion of both a minified and unminified version of a script.

... src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js">
... src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">  
But I'll get to it, when I have time.

Useful Resources

YSlow rule #13 – Remove Duplicate Scripts
Introduction to Visual Studio 2010 Extensibility
Manipulating Project Files in VS
Customizing Visual Studio Extension Icon in Visual Studio 2010

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Omar Gameel Salem
Software Developer
Australia Australia
Enthusiastic programmer/researcher, passionate to learn new technologies, interested in problem solving, data structures, algorithms AI, machine learning and nlp.
 
Amateur guitarist/ keyboardist, squash player.
 
If you have a question\suggestion about one of my articles, or you want an algorithm implemented in C#, feel free to contact me.
Follow on   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.1411028.1 | Last Updated 30 Jul 2012
Article Copyright 2012 by Omar Gameel Salem
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid