views:

1000

answers:

5

I have a Scripts folder, that includes all the .js files used in the project. Using the Ajax Minifier task, I generate .min.js files for each one. Depending on whether the application is running in debug or release mode, I include the original .js file, or the minified one.

The Scripts folder looks like this:

Scripts/script1.js
Scripts/script1.min.js   // Outside the project, generated automatically on build
Scripts/script2.js
Scripts/script2.min.js   // Outside the project, generated automatically on build

The .min.js files are outside the project (although in the same folder as the original files), and they are not copied into the destination folder when we publish the project.

I have no experience whatsoever using build tasks (well, apart from including the minifier task), so I would appreciate if anyone could advise me as to which would be the correct way to:

  • Copy the .min.js files to the destination folder when I publish the app from Visual Studio.
  • Delete / Not copy the original js files (this is not vital, but I'd rather not copy files that will not be used in the app).

Thanks,

Edit: From the responses, I see that I missed some details, and that maybe I'm looking for the wrong solution to the problem. I'll add the following details to the question:

  • If possible, we'd rather not create copy scripts in the build process of the solution. Sure, we considered it, but we were using Web deployment projects up until now, and we'd rather start using the new Publish feature of VS2010 (which is supposed to replace these projects) than manually adding copy commands to the build task.
  • The *.min.js files are not included in the project because they can't be in the Source control system (TFS at this moment). They are files generated during compilation, and it would be akin to including the 'bin' folder in the TFS (including the problems it would cause). Maybe we should create the min files in a different folder and treat it like 'bin'? (Is that even possible?)
+2  A: 

You probably want to take a look at Build Events, with which you can specify a command line to execute post-build. Your question is, additionally, similar to these questions.

For the actual copying, you can quite easily just make use of the COPY command.

James Burgess
Thanks for the answer but, if possible, we'd rather use the new Publish function of VS2010, as it includes some features we want to use in the future.
salgiza
+2  A: 

If you want to do Javascript minification via Visual Studio, this will get you started: http://encosia.com/2009/05/20/automatically-minify-and-combine-javascript-in-visual-studio/

Otherwise, I'd recommend a tool that can automatically combine and minify Javascript. The two tools that I have looked at are Justin Etheredge's Bundler and Combres. I'm using Bundler in my current project and one of my work colleagues is using Combres. Bundler is a little bit simpler to use but it does less than Combres. With Bundler if debug is turned off in web.config then it doesn't minify and combine which means that you can debug the javascript in your dev environment.

Daniel Lee
Thanks, but minifying is not the problem (we used YUI in the past, and recently migrated to Microsoft's minifier). We kind of expected that Microsoft would consider that people would use it's own minifier when creating the Publish option of VS2010, though :S
salgiza
Just keep in mind that combining your scripts into one javascript file is nearly as important as minifying them. It speeds things up if you can keep the number of HTTP requests down.
Daniel Lee
+1  A: 

I am not quite sure whether this approach would be useful in this situation but I could imagine it... You can modify csproj files to e.g. influence what files should be included in a certain type of build.

With certain type I am referring to the Configuration Manager.

Debug and Release are the default ones but nothing stops you from making new ones. Based on the selected configuration you can have different files being part of the configuration and I suppose that you could publish different files that way. I have describds that strategy on my blog here : http://realfiction.net/go/130

flq
Mmmm... that's an interesting solution, but would it solve the problem of not wanting to include the *.min files in the TFS/CSV? (I've edited the question, as I forgot to mention that this was the reason to exclude the minified files from the project).
salgiza
A: 

This article about "Automatically Minify, Combine, Compress, and Cache *.js and *.css Files in your ASP.NET Project" can maybe help you

http://www.codeproject.com/KB/aspnet/CssAndJavaScriptOptimizer.aspx?display=Print

Ivo
+3  A: 

Rather than implement this at build time, I suggest you do so at runtime. This has a number of advantages:

  • You can include debugging parameters that turn off the combining and minification to permit easier identification of errors. This also allows you to run with less difference between development and production environment.
  • You gain some flexibility. I've twice been able to fix errors via a script-only fix that can go live directly. For simple but critical errors that's a nice option.
  • It's quite a bit simpler to implement - you already have the expertise in implementing http responses, which applies nicely here.
  • You can track the last-modified date of the scripts involved, and not only use that to set the appropriate ETags and whatnot (which IIS can do too), but rather set a far-future expiry date. Then, rather than linking the actual scipt (whether minified or not), you can link the script with some short token in the querystring - that way clients don't need to check whether the js has updated. When it has, the pages will link to a "new" script file that needs to be separately requested anyhow. (This is possible to do in build scripts, but trickier).
  • A complex build process often has hidden costs. Not only does it take longer to run, but what happens when you want to update your build automation tool? When you switch IIS or windows versions? When you migrate to VS 2010? How easy is it to bring new developers up to speed?

This is the rough outline of the process I follow:

  1. I specify two directories as containing only compressible css and js. At appdomain instantiation or shortly thereafter via a static constructor, a class finds the contents of those directories and creates a FileSystemWatcher to watch for changes.
  2. All files are read in order of filename (using prefixes such as 00_jquery.js 10_init.js etc. helps control order here). The list of filenames is stored for debug purposes, and the l
  3. All files are combined via string concatenation, then minified by YUI, then compressed via GZipStream. A version specific token is computed either by newest last-modified date or by the hash of the result.
  4. The result of the compression (byte array, filenames, and version-specific token) is stored in a static class variable (protected by a lock). If the file system watcher detects an update, step 2 starts again and runs in the background until the compression completes - hence the locking.
  5. Any page wishing to include the combined javascript (and/or css) calls the shared static variable. If we're in debug mode, that generates a script (or link) tag for each filename as stored in step two, otherwise, this generates a single script (or link) tag to a Uri that's handled by a custom IHttpHandler. All Uri's include the version-specific token in the querystring - this is ignored both by the IIS static file handler and the custom http handler for the combined minified version, but makes caching easy.
  6. In a custom IHttpHandler, when a request for the combined javascript (or css) is received, the Content-Encoding: gzip header is set, and a far-future Expiry date. Then the precompressed byte array is written directly to the http stream via context.Response.OutputStream.

Using that approach, you won't need to fiddle with web.config options whenever you add or remove a script file; you can update scripts while the app is running and clients will request these on the very next page view - but you still will get optimal cache behavior since browsers won't even send an If-Not-Modified request due to the expiry header. Usually, compressing scripts should take a second or so and the compressed result should be so small that the memory overhead of the static variable is negligible (at most a few 100 KB for a truly large amount of scripting / css).

Eamon Nerbonne
It's not exactly the answer I'm looking for, but it's certainly an interesting solution (and it certainly has a few advantages). I guess that if it's impossible to use Microsoft's Minifier + Microsoft's TFS + Microsoft's Visual Studio, this is going to be the solution :/
salgiza
Why would you want to do this at build time though - what's the advantage? It's almost certain to be more work, more hassle, and less flexible to boot.
Eamon Nerbonne
It's going to be less flexible, but taking into account that using the Minimizer consists in adding three lines to the XML of the project, I wouldn't say it's more work! ;)
salgiza
That being said, it solves the problem, and it's the best answer! Although it annoys me to no end that the solution to "how to use Microsoft's minimizer task" is "not to use it" (aaaarg! so much for spending time migrating from YUI to it because it was supposed to integrate better with Visual Studio).
salgiza