Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Redundant References Remover VSX

0.00/5 (No votes)
29 Jul 2012 1  
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 ;)

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here