29 Comments

imageThe _references.js file controls a key part of the JavaScript Intellisense in Visual Studio for web projects and can mean the difference between awesome Intellisense and none at all.

This is the story of how it works and why it even exist.

A new JavaScript editor

The story begins shortly after the release of Visual Studio 2010. The ASP.NET & Web Tooling Team (the team I'm part of) was handing over the ownership of the JavaScript editor to a newly formed Client Platform team.

This team was going to create a completely new and modern JavaScript editor for Visual Studio 2012 – one that was aligned with the efforts around delivering the tooling experience for the upcoming HTML/JavaScript based Windows 8 Store apps.

The now old JavaScript editor was retired from Visual Studio, but found new life in WebMatrix, where it received further development and updates.

As the work progressed with the new editor, it was time to redesign some of the many features we've all gotten accustomed to. One of them was Intellisense.

Three types of Intellisense

There are several ways of implementing Intellisense for JavaScript. The three I remember being discussed were:

  1. All .js files in the project are automatically included in Intellisense
  2. Only .js files included on the same HTML pages are included
  3. The user can manually reference other .js files

By including all .js files in Intellisense means that there is potential for a lot of false positives. By that I mean that you'll get Intellisense coming from .js files that are not loaded on the same page as the .js file you're currently editing. It's sort of the same as getting C# Intellisense for types that hasn't been imported with a using statement – it would break at runtime.

Loading all .js files automatically in Intellisense also came with additional issues. One was performance. The more files to parse, the heavier Intellisense became and it could hurt the user experience. Another was ordering of the .js files. The new editor runs all the code, so correct ordering of when to run each .js file was an important factor for accuracy.

However, bundling, minification and dynamically loaded JavaScript would make it impossible for any IDE to statically figure out what .js files to provide Intellisense for. The result could potentially be that no Intellisense from other .js files got loaded at all, which was arguably worse than loading too much Intellisense.

Finally, letting the user manually add references to other .js files eliminates performance issues and the parsing of HTML files to follow <script> references. For ASPX files that also meant following references to master pages to find <script> references. The problem with relying on the user to add references is that it puts the burden on the user.

So there were pros and cons to each approach.

Explicit and implicit references

It was decided that the new editor should use a combination of option 2 and 3. It both follows <script> references and allows the user to manually add an explicit triple-slash references. Triple-slash references look like this and can be added to the top of any .js file:

/// <reference path="../app/respond.js" />

Dragging a .js file from Solution Explorer onto an open .js document will automatically insert a triple-slash reference at the top of the document. By adding this reference, Visual Studio will now always include Intellisense coming from the referenced file, regardless of any other conditions.

_references.js is global

The issue with explicitly referencing other .js files is that you'll end up adding reference to jQuery more or less on all your .js files. That's not cool. It was important to keep the burden on the user at a minimum, so a global reference cache was needed.

Enter _references.js.

This file must (by default) be located in a folder at the root called /scripts/. That's the naming convention. Any file located at /scripts/_references.js is automatically added to global Intellisense. This is now the only file we need for triple-slash references. Here's what the contents of this file may look like:

/// <reference path="modernizr-2.6.2.js" />
/// <reference path="jquery-1.10.2.js" />
/// <reference path="bootstrap.js" />
/// <reference path="respond.js" />

Just a bunch of references. This is also the only file that is included in Intellisense by default at all times.

The cool thing is that the user now only have to control Intellisense in one central location. Burden minimized.

Change naming convention

Some people don't like to use a folder named /scripts/ or the name _references.js. That's perfectly fine and Visual Studio offers a way out.

Let's imaging that someone prefers the global reference file to be located at /js/globals.js – not just for one project, but in general.

Get to the Quick Launch box by hitting CTRL+Q. Then type javascript ref and select this option in the dropdown:

image

In the settings window that pops up, select Implicit (Web) in the dropdown:

image

Implicit (Windows) is for Windows Store apps and has no effect on web projects. The same is true vice versa.

At the bottom of the list, you can see that there is a reference to /Scripts/references.js:

image

So now you can see what the path syntax looks like and you can therefore easily add a new path ~/js/globals.js. You can add as many as you'd like. If the file doesn't exist, Visual Studio will just ignore it.

A tilde followed by a forward slash (~/) is referred to as tilde-slash and marks the root of a website.

Automating it all

All this shipped with Visual Studio 2012, but when it was time to focus on Visual Studio 2013, we decided that this workflow should be improved.

It wasn't ok that developers should maintain this file manually. JavaScript files are added, deleted and renamed all the time, so it's very easy to forget to update the _references.js file accordingly.

A better approach was to add a feature that would do that automatically and thereby give Intellisense for all .js files globally all the time. However, we knew that both performance and file ordering could be an issue, so it was important that this feature could be disabled in case of trouble.

We didn't want a setting, since Visual Studio doesn't have per-project settings. And this was indeed a per-project challenge. So we introduced a new triple-slash concept – auto-sync.

It looks like this and is located at the very top of _references.js:

/// <autosync enabled="true" />

This new auto-sync comment is all it takes to enable automatic synchronization of triple-slash references. Whenever a .js file is added, renamed, moved or deleted, the _references.js file is automatically updated to reflect the changes.

Right-clicking on the _references.js file in Solution Explorer now gives you two new options:

image

The first is the Auto-sync JavaScript References. Clicking this menu item adds the auto-sync comment so you don't have to type it manually. It's convenient, discoverable and you only have to set it once per project.

The second menu item is Update JavaScript References. This will add all .js files in the project as triple-slash references in the _references.js file. It updates only once and it doesn't enable auto-sync.

With these two new commands, you can now decide if you want auto-sync or if you prefer to just update the references whenever you are in the mood.

One caveat to enabling auto-sync of _references.js is that the references are potentially ordered in the wrong way. It could cause issues, but it probably won't. Personally, I haven't had any issues and I've used this in many projects now. But just in case it does cause trouble, now you know how to turn it off.

So with Visual Studio 2013 we end up having all three approaches to JavaScript Intellisense.

Generate a _references.js file

If you don't have a _reference.js file in your project, but you have Web Essentials 2013 installed, then it's really easy to add one. Simply right-click the /Scripts/ folder and go to the Add submenu to find an easy way to add references.js.

_references

An added benefit with this approach is that the new _reference.js file has auto-sync enabled already.

So, that was the story of _references.js and how it came to be. There are always room for improvements, so our job is never done. The work continues…

Comments

Comment by Harry M

Does autosync take ///references in script files into account when sorting the files?

Harry M
Comment by Donatas

The big issue for our team with _references.js is that VS 2013 only uses references defined in the first 10240 characters of it. We have many small files and it's really easy to reach this limit. It only takes about ~110 entries or so in our case and we have 300 .js files that we'd like to have intellisense for.

Donatas
Comment by Brent

Any way to effect set the javascript reference for an entire project so each member of the team does not need to update their VS settings?

Brent
Comment by Mads Kristensen

@Harry, auto-sync just lists the /// references like they are located in the file system. No other sorting takes place.

@Donatas, we've already addressed some issues with huge projects. The 102400 character limit you mention is a first, and we've created a bug for that. Fixes are coming in a VS Update at some point next year. Thanks for stress testing the feature with your 300 .js files :)

@Brent, no unfortunately no. Settings in Visual Studio are personal and doesn't roam with the project. It's a feature I'd like to see, so if you do too please vote for it here visualstudio.uservoice.com/.../2089769-store-per-project-source-formatting-settings-with-

Comment by Christopher Brown

It seems like this is a scenario when you could have used something like requireJs, rather than including a ".JS-but-only-comments" file that controls the IDE only. If Visual Studio can pick up "using" statements in C#, surely you could do the same with a require() function call.

Christopher Brown
Comment by Mads Kristensen

@Christopher, wouldn't the end result be the same? The user is still burdened with maintaining one file. I'd argue that the triple-slash reference syntax is simpler than requireJS

Comment by Christopher Brown

@Mads Simpler for whom? For the developer on a greenfield product, maybe it's a wash, because they get their intellisense either way. For the developer who's maintaining the codebase 3 months after it's been released, I think having references explicit and testable is drastically simpler.

Christopher Brown
Comment by Mads Kristensen

@Christopher, I think I misunderstood you before then. I agree, if you're using requireJS, then it should just work. We're actually working on improving the Intellisense support for requireJS and AMD in general. That would take care of the issue

Comment by Cory

Will the Update JavaScript References menu item exclude *.min.js files?

Cory
Comment by Mads Kristensen

@Cory yes, it has logic to only take the un-minified .js file and ignore *.min.js.

Comment by Donatas

Thanks Mads! Good to hear the fixes are comming :)

Donatas
Comment by Ryan Williams

Is changing the name convention something that can only be done on a per-environment basis, meaning every developer working on the project has to duplicate it in order to be compatible with a project that stores its references in, say, /resources/js/? Or can this be embedded into the solution?

(Got an 'undefined' error when I tried to post this comment before, hope it doesn't appear twice.)

Comment by Tom Robinson

Is it possible to reference scripts outside of the project, e.g. in other projects or solutions? And is there a way of referencing embedded resources in other assemblies?

Comment by Mads Kristensen

@Tom, you can add an absolute path to the triple-slash references, so you can references .js files in other projects easily. You cannot reference embedded resources

Comment by John

Hi Mads, Is this an approach that could also be applied to svg intellisense (ie auto populating the Schemas directory with the correct xsd's)? PS Liking the new svg preview feature as well.

John
Comment by Mads Kristensen

@John, not exactly. VS handles XML and their schemas in a completely separate way through the registry. The XML editor team would have to make it extensible so we can provide the same level of support - it's not something that can be done in Web Essentials or by the Web Team alone

Comment by Jianwei

With _references.js, do we still need *.intellisense.js files?

Jianwei
Comment by Mads Kristensen

@Jianwei, Yes you do.

*.Intellisense.js files gives more precise information about the various functions and objects, whereas _references.js tells VS which .js files to gather Intellisense from.

Comment by Jianwei

@Mads,

Thank you for your answer, how about *.vsdoc.js , are they serve the same purpose of *.Intellisense.js?

Jianwei
Comment by Mads Kristensen

@Jianwei, the *-vsdoc.js files were introduced in 2010, but when we wrote the new JavaScript editor for 2012, we upgraded the capabilities through *.intellisense.js. They are similar, but *.intellisense.js can do more than *-vsdoc.js. Just think of *.intellisense.js as version 2.0 of *-vsdoc.js

Comment by Jianwei

@Mads,
Great explanation, just to make sure, in _references.js, I will include the NORMAL *.js file, while *.intellisense.js and *-vsdoc.js will be used by Visual Studio to display more intellisense information for the normal javascript file. Sorry for so many questions, but this has been confusing to me for a while.

Jianwei
Comment by Jianwei

@Mads, thank you for taking time to answer so many questions.

Jianwei
Comment by Jianwei

@Mads,

I have another question, if I already have *.intellisense.js or *.-vsdoc.js, does it make sense to reference the *.min.js file or the normal js file.

Visual studio by default reference jquery-1.10.2.js, and I wonder whether I could reference jquery-1.10.2.min.js by renaming jquery-1.10.2.intellisense.js to jquery-1.10.2.min.intellisense.js .

The whole setup seems a bit confusing to me with too many files put down by wizard.

Jianwei
Comment by kk

@Mads

Is it possible to exclude certain javascripts from being automatically added like everything in ~/scripts/plugins ?

kk
Comment by Mads Kristensen

@kk, no it isn't. It's all or nothing

Comment by qbantek

Nice job!
Small suggestion: Build Action for _reference.js should default to None (currently: Content)

qbantek
Comment by Julius Depulla

After installing Web Essentials on VS 2013. I have build errors on *.js files that are used for intellisense. Do you have a "how to ..." on excluding these files during the build? It will be a good reference for anybody that uses Web Essentials, especially for the first time

Julius Depulla