77

In case the question wasn't clear. I have 3 MVC projects in one Solution. Every time I create a new project it adds the "Scripts" folder with all the .js files I'll ever need. I don't want to have this created every time for every application. Is there a way to reference scripts from a central folder in the solution so all applications/projects can share one common script folder with all the scripts common among them?

Edit: Please explain the pros and cons of doing this if there are any...now I'm curious.

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
EKet
  • 7,272
  • 15
  • 52
  • 72
  • This doesn't work for folders, but you can link a file from another project. Right click a folder and Add an Existing Item, find your file in another project, click the arrow next to the Add button and choose "Add as Link". The file won't physically exist in the correct location until you deploy, so this won't work for static script files while debugging in Visual Studio. – Nick VanderPyle Dec 16 '11 at 21:32
  • I am surprised Erik Philips's answer got so many likes without mentioning the issue that is discussed here: https://stackoverflow.com/questions/4166975/add-as-link-for-javascript-files-returning-404-in-debug Unfortunately I am not being allowed to add it as a comment so adding as a solution instead. – Murometz80 Nov 08 '21 at 16:39

7 Answers7

121

Here is what I would recommend:

Right click the solution and create a New Solution Folder called Common Javascript Files (or whatever you feel like calling it.

New Solution Folder

Common Javascript Files Solution Folder

Right click on the Solution, click Open Folder in Windows Explorer, or navigate there manually for other versions of Visual Studio :(

Open Folder In Windows Explorer

In the solution directory, create a directory with the same name as the solution folder (solution folders do not normally match directories at the source code level but this will for sanity sake).

Common Javascript Files Directory

In this new directory, add files that need to be shared between solutions.

Add Javascript Files To Directory

In Visual Studio, click the solution folder and select Add - Existing Item.

Visual Studio Add - Existing Itme

In the file selection dialog, navigate to the directory previous created, select the file(s) added to the directory and click Add.

Select Files To Add

Solution Folder Files

In each Project that needs a shared file, right click on the project (or directory within the project) and click Add - Existing Item.

Project Add Existing Item

Navigate to the shared Directory, Select the files and click the drop down arrow then click Add As Link.

Add As Link

Now the files in the projects are essentially short cuts to the files in the Solution Folder. But they are treated as actual files in the project (this includes .CS or Visual Basic files, they will be compiled as files that actually exist in the project).

Linked Files

PROS

  • Files are truly shared across projects at Design time
  • Only the files needed for each project can be added, it's not all or nothing
  • Does not require any configuration in IIS (virtual directory etc)
  • If the solution is in TFS Source control, you can add the Directory to the TFS Source and the shared files will be source controlled.
  • Editing a file by selecting it in the Project, will edit the actual file.
  • Deleting a Linked file does not delete the file.
  • This is not limited to JS files, linked files can be ANY file you might need (Images, Css, Xml, CS, CSHTML, etc)

CONS

  • Each deployment gets it's own file.
  • There is a small learning curve when understanding that Solution Folders are not Directories that exist in a Solution Directory.
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • 39
    It is also useful to note that linked files will not be accessible during debugging, unless a task is added to the Project File to manually copy the linked files [as described here](http://mattperdeck.com/post/Copying-linked-content-files-at-each-build-using-MSBuild.aspx). – Mac May 20 '14 at 19:16
  • Is the first step (creating the solutions folder) really necessary? I find it a bit cumbersome to manage both files on disk AND the files in the solution folders. – Carl Björknäs Apr 28 '15 at 07:55
  • It's not necessary, but it's recommended. If you're using source control, you'll want the files somewhere in your solution folder. If not, it makes no difference where the files are. – Erik Philips Apr 28 '15 at 14:34
  • 1
    When i publish the project, the files are not copied to the folder structure i created. Any idea how to solve this? – Fonsini Jul 20 '15 at 13:33
  • 2
    @Fonsini Set the properties of the linked files correctly. – Erik Philips Jul 20 '15 at 14:27
  • @Mac Simply brilliant, sir. I worked on this for about an hour before I saw this code somewhere else, but I disregarded it as too invasive....After working on it for another 5 hrs I finally implemented this and it was SO EASY!! Works like a charm!! – Ross Brasseaux Oct 04 '15 at 00:57
  • 1
    The problem I am experiencing is when trying to include the JS files in HTML, it wants to resolve to the actual folder - not the linked folder. That is not practical at all. – MarzSocks Nov 05 '15 at 07:22
  • 1
    You might be interested to know that this answer is being (indirectly) discussed [here on meta](http://meta.stackoverflow.com/questions/323545/stealing-someones-answer). – Ilmari Karonen May 22 '16 at 22:34
  • 2
    I like this method but I am unable to get it working with for example a CSS file I want to share between two projects. I have made separate folder, copied CSS there and add this CSS then as link to the Content folder. Problem is that the BundleConfig does not resolve this and hence does not add the CSS link to the head. I have tried selecting "Copy Always" also on the link properties but with no luck. Is this a problem because I run it in Visual Studio when developing, and in that case no actual copying happens? – Johncl Aug 14 '16 at 13:48
  • 1
    Yes, I found the way to do the copying as well. Only problem with this solution is that you cannot edit e.g the CSS file and hit F5 to check the changes - as you have to stop and start in order for the files to be copied again. So close but no cigar... Also the fact that the files are copied in means the copies end up in source control unless you add .gitignore or whatever means to filter out files. – Johncl Aug 16 '16 at 07:16
  • Which file do you reference? I can't get the path to the folder outside the solution right, if it's even is possible? And reference the shortcut file, doesn't work. "ReferenceError: testHelper is not defined testHelper.initialize();" – radbyx Oct 27 '16 at 10:55
  • 2
    This solution is nice but you can't use it in pair with Asp.net bundles : The files just won't be bundled. Since bundles are the proper way of handling client resources in asp.net projects, it's a pretty big con. – Jérôme S. Feb 09 '17 at 11:10
  • @Johncl To solve the problem of the files not being updated on edit, I wrote a powershell script that watches all the files in the Common folder and updates the project files when they change. I modified the action in the script from [this answer](https://superuser.com/a/844034/600259) by a robocopy action : `$path = $Event.SourceEventArgs.FullPath $yourProjectPath = $path -replace "Common Folder", "%ProjectFolder%" robocopy $path $yourProjectPath ` – WhiteFangs Mar 10 '17 at 13:48
  • @Johncl its 2021 did you find a nicer way to do this by any chance? – Seabizkit Jul 20 '21 at 11:29
  • @Seabizkit I am sorry but its been years since I have worked on these kind of projects, mainly using more modern JS frameworks now where auto-reload is an important part of the workflow. Development friction is a killer and many .net projects are riddled with them imo. – Johncl Jul 21 '21 at 12:44
  • Creating a linked file in this way and setting it to `CopyAlways` in the file properties will put a copy of the file into the `bin` folder. In the above example the linked file is placed in the "Scripts" folder and therefore on build we'll see the files in "bin/scripts". Use a post-build action like `copy "$(ProjectDir)\bin\scripts\*.*" "$(ProjectDir)\scripts"` to copy the file to the actual location so that C# bundling functions can grab a source file and not 404 – JDandChips Jan 13 '23 at 11:29
  • For anyone running into issues with linked files not being included in builds or debug sessions check out this nuget packages: https://www.nuget.org/packages/MSBuild.WebApplication.CopyContentLinkedFiles/ This package adds MSBuild target which copies all content files added as link to Web Application folder during build. – Nick Van Brunt Jan 26 '23 at 15:07
24

The best thing to do, imo, is to roll your own CDN... Basically just create another site in IIS and give it it's own binding, e.g. "http://cdn.somedomain.com"

Then store all of your css/js/fonts/shared images etc on the CDN site and link to them from your other sites.

Doing so solves 2 problems,

  1. All of your stuff is shared when it needs to be and you only have to manage 1 revision per file.
  2. Your users browsers can cache them in 1 single location instead of downloading copies of your stuff for every site that uses them..

I added this answer because I see a lot of people referrencing creating virtual directories. While that does indeed share the files, it creates multiple download paths for them which is an extreme waste of bandwidth. Why make your users download jquery.js (1 * number of sites) when you can allow them to download it once on (cdn.somedomain.com).

Also when I say waste of bandwidth, I'm not just talking about server bandwidth, I'm talking about mobile users on data plans... As an example, I hit our companies HR site (insuance etc) on my phone the other day and it consumed 25mb right out the gate, downloaded jquery and a bunch of stuff 5 times each... On a 2gb a month data plan, websites that do that really annoy me.

Ryan Mann
  • 5,178
  • 32
  • 42
  • Thought I would add that to link to them, use the //cdn.somedomain.com syntax if you have both http/https configured, it will default to whatever the includer is. Also if you have a dev CDN and a Production cdn, you can store the cdn url in appSettings e.g. ProdCdnUrl and DevCdnUrl, then use conditional compilation to select the one for the project's build configuration, e.g. #if DEV getdevurl #if PROD getprodurl... Then use it in your razor template something like script src="@(cdnUrl)/lib/bootstrap..." – Ryan Mann Jan 26 '15 at 21:37
  • Thank you for the new insight given in your Feb. 2016 comment. – Mike Grove aka Theophilus Feb 24 '18 at 19:55
11

Here it goes, IMO the best and easiest solution, I spent a week trying to find best and easiest way which always had more cons than pros:

Resources(DLL)
  Shared
    images
      image.png
    css
      shared.css
    scripts
      jquery.js


MvcApp1
  Images
  Content
  Shared <- We want to get files from above dll here
  ...

MvcApp2
  Images
  Content
  Shared <- We want to get files from above dll here
  ...

Add following to MvcApp1 -> Project -> MvcApp1 Properties -> Build events -> post build event:

start xcopy "$(SolutionDir)Resources\Shared\*" "$(SolutionDir)MvcApp1\Shared" /r /s /i /y

Here is explanation on what it does: Including Build action content files directory from referenced assembly at same level as bin directory

Do the same for MvcApp2. Now after every build fresh static files will be copied to your app and you can access files like "~/Shared/css/site.css"

If you want you can adjust the above command to copy scripts from .dll to scripts folder of every app, that way you could move some scripts to .dll without having to change any paths,here is example:

If you want to copy only scripts from Resources/Shared/scripts into MvcApp1/scripts after each build:

start xcopy "$(SolutionDir)Resources\Shared\Scripts\*" "$(SolutionDir)MvcApp1\Scripts" /r /s /i /y
Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
formatc
  • 4,261
  • 7
  • 43
  • 81
  • 3 questions: 1) What happens if a file is deleted from the Resources/Shared - it doesn't look like it will be deleted from the MvcApp copy of the folder? 2) Is there any way to get Visual Studio to delete these files from `$(SolutionDir)` after run completes. Leaving them there might confuse other devs. 3) You have (DLL) in brackets after the Resources folder. Any specific way to set this up? – Adam Nov 21 '13 at 12:06
  • 1) They would of course stay in folder, since we are only copying them 2) The solution would be to delete all files in folder before copying, you would add something like this before xcopy: http://stackoverflow.com/questions/768282/how-to-delete-files-in-visual-studio-pre-build-event-command-line , or write a batch script that checks files two folders and deletes differences and call it using like: http://stackoverflow.com/questions/12764847/visual-studio-post-build-events-calling-batch-files-with-arguments-that-have-spa 3) It is for demonstration only, its a dll project called Resources – formatc Nov 21 '13 at 17:29
  • @Adam see above comment. – formatc Nov 21 '13 at 17:35
  • Anytime you edit a shared resource, you'd need to build the project to see the changes, right? – crush Jun 25 '15 at 14:48
  • @crush Yes,but you can create batch script and put those commands in it, then replace post build event to start your script, and you can manually start it, maybe there is "macro" or something in VS that would let your run script on file change. – formatc Jun 25 '15 at 14:52
  • That was a week well spent and shared with the world. – jimjim Jun 14 '16 at 05:38
6

This is a late answer but Microsoft has added a project type called Shared Project starting Visual Studio 2013 Update 2 that can do exactly what you wan't without having to link files.

The shared project reference shows up under the References node in the Solution Explorer, but the code and assets in the shared project are treated as if they were files linked into the main project.

"In previous versions of Visual Studio, you could share source code between projects by Add -> Existing Item and then choosing to Link. But this was kind of clunky and each separate source file had to be selected individually. With the move to supporting multiple disparate platforms (iOS, Android, etc), they decided to make it easier to share source between projects by adding the concept of Shared Projects."

https://blogs.msdn.microsoft.com/somasegar/2014/04/02/visual-studio-2013-update-2-rc-windows-phone-8-1-tools-shared-projects-and-universal-windows-apps/

Info from this thread:

What is the difference between a Shared Project and a Class Library in Visual Studio 2015?

https://stackoverflow.com/a/30638495/3850405

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • This sounds great on paper, but I've yet to see an example anywhere of Shared Projects being used successfully with JavaScript files. You can create the shared project sure, and put your scripts there, but the web project seems to fail to recognize them properly. – Nathanael Aug 24 '20 at 22:31
3

A suggestion that will allow you to debug your scripts without re-compiling the project:

  • Pick one "master" project (which you will use for debugging) and add the physical files to it
  • Use "Add As Link" feature as described in Eric's answer to add the script files to the other projects in solution
  • Use CopyLinkedContentFiles task on Build, as suggested in Mac's comment to copy the files over to the second over to your additional projects

This way you can modify the scripts in the "master" project without restarting the debugger, which to me makes the world of difference.

DivineOps
  • 1,656
  • 16
  • 17
  • Can you explain how it is possible to modify "master" project files and make the changes available while the app is running without restarting the debugger? Linked files are only copied on build and you cannot build without stopping the debugger in the first place. – pbalaga Apr 04 '16 at 08:33
  • If you add physical files to the "master" (not just the links) you can modify the master without restarting. In other projects the linked files will be copied on build, but that's of a lesser impact, as I spent most of my time running the master. – DivineOps Apr 05 '16 at 14:37
  • This should be marked as answer because there is so less hassle! The modification to make to the .csproj is this one: – barbara.post Mar 30 '17 at 14:39
2

In IIS create a virtual folder pointing to the same scripts folder for each of the 3 applications. Then you'll only need to keep them in a single application. There are other alternatives, but it really depends on how your applications are structured.

Edit

A scarier idea is to use Areas. In a common area have a scripts directory with the scripts set to be compiled. Then serve them up yourself by getting them out of the dll. This might be a good idea if you foresee the common Area having more functionality later.

Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142
  • That's a great idea when the project is on the server. What about when the project is still on the development box in Visual Studio? – Nick VanderPyle Dec 16 '11 at 21:26
  • @NickVanderPyle same idea would work. Should be able to set it up, regardless of whether you're using loopback adapters and different sites or just different applications. – Yuriy Faktorovich Dec 16 '11 at 21:29
  • Oh, I see. It'll work if you're running and debugging with IIS locally and can use virtual folders. Good call. – Nick VanderPyle Dec 16 '11 at 21:35
  • What do you think of an NTFS junction point if IIS isn't available? – Nick VanderPyle Dec 16 '11 at 21:37
  • @NickVanderPyle you'd have to remove the scripts directories from 2 of the projects, but yes, that also sounds pretty good. – Yuriy Faktorovich Dec 16 '11 at 21:43
  • Would this be considered good practice? Or does it really matter if I have the same scripts in every project. – EKet Dec 16 '11 at 22:00
  • @EKet It depends on what kind of scripts we're talking about here. If it is scripts like jquery, then it doesn't matter if its in every project. If it is something you write yourself and will have the tendency to get out of sync between projects, then I'd go with finding a common solution. Also if source control and teammates are involved in the picture. I always like to figure out a way to do it DRY if possible. – Yuriy Faktorovich Dec 16 '11 at 22:05
  • It's the 10 or so .js files you get when you create a new MVC 3 with Razor Project. It's just too many and wasteful to have it in every place I feel. Perhaps it's just OCD to hate seeing "shared" code be in every project instead of a link like a library. – EKet Dec 17 '11 at 00:08
  • @EKet I'd go with jbrunken's solution. – Yuriy Faktorovich Dec 17 '11 at 01:19
1

Most of the files that are included by default are also available via various CDN's.

If you're not adding your own custom scripts, you may not even need a scripts directory.

Microsoft's CDN for scripts: http://www.asp.net/ajaxlibrary/cdn.ashx

jbrunken
  • 294
  • 1
  • 9