Dave Paquette

Caffeine Driven Development

Waiting for gulp tasks to finish in Visual Studio

February 23, 2015

In a previous post, I outlined how to use Bower in Visual Studio 2013. While this code works for the typical developer workflow, a reader pointed out that errors were observed when attempting to publish to Azure using the Visual Studio publish dialog.

After further investigation, I was able to determine the reason for this error is due to the way Task Runner Explorer executes tasks. While we setup the binding so that our default Gulp tasks runs Before Build, this does not fully integrate into the Visual Studio build pipeline. The Gulp task is indeed triggered before Visual Studio starts to build the project, but Visual Studio does not wait for the task to complete before building the project. As a result, if your VS project is small enough, Visual Studio might think the build is completed before our Gulp task is completed. This is the reason for the Publish failing: Visual Studio was proceeding with publishing to Azure before all the required files were generated.

A Potential Workaround

Eventually, there will be better support for this in future versions of Task Runner Explorer. In the meantime, we need a simple workaround. One option is to run a script as a Post Build Event for my project to wait for all the generated files to exist. I did this as a simple PowerShell script file named WaitForFiles.ps1:

$start = $(Get-Date)
$rootPath = $args[0]
 
#Files to wait for
$bootstrapBundle = $rootPath + "Scripts\bootstrap-bundle.min.js"
$jqueryBundle = $rootPath + "Scripts\jquery-bundle.min.js"
$modernizer = $rootPath + "Scripts\modernizer-min.js"
$css = $rootPath + "Content\dist\css\app.min.css"
$glyphicons = $rootPath + "Content\dist\fonts\glyphicons-halflings-regular.*"
 
#Delay 2 seconds to ensure the gulp tasks were started (ie. Make sure we aren't testing for files before they were deleted and regenerated)
Start-Sleep -Seconds 2
while(!((Test-Path $bootstrapBundle) -and (Test-Path $jqueryBundle) -and (Test-Path $modernizer) -and (Test-Path $css) -and (Test-Path $glyphicons))) {
    Write-Output "Waiting for generated files"
    Start-Sleep -Seconds 2
    $elapsedTime = $(Get-Date) - $start
   #Wait a maximum of 60 seconds for the files to appear
    if ($elapsedTime.TotalSeconds -gt 60){
        Write-Error "Timed out while waiting for generated files"
        exit -2
    }
}
exit 0
 
$start = $(Get-Date)
$rootPath = $args[0]

#Files to wait for
$bootstrapBundle = $rootPath + "Scripts\bootstrap-bundle.min.js"
$jqueryBundle = $rootPath + "Scripts\jquery-bundle.min.js"
$modernizer = $rootPath + "Scripts\modernizer-min.js"
$css = $rootPath + "Content\dist\css\app.min.css"
$glyphicons = $rootPath + "Content\dist\fonts\glyphicons-halflings-regular.*"

#Delay 2 seconds to ensure the gulp tasks were started (ie. Make sure we aren't testing for files before they were deleted and regenerated)
Start-Sleep -Seconds 2
while(!((Test-Path $bootstrapBundle) -and (Test-Path $jqueryBundle) -and (Test-Path $modernizer) -and (Test-Path $css) -and (Test-Path $glyphicons))) {
    Write-Output "Waiting for generated files"
    Start-Sleep -Seconds 2
    $elapsedTime = $(Get-Date) - $start
   #Wait a maximum of 60 seconds for the files to appear
    if ($elapsedTime.TotalSeconds -gt 60){
        Write-Error "Timed out while waiting for generated files"
        exit -2
    }
}
exit 0
 

Now, execute this script in the Post Build event of the web project. You can set the post build event from the Build Events tab of the Project Properties page.

 

image

Powershell -ExecutionPolicy bypass -File "$(ProjectDir)WaitForFiles.ps1" "$(ProjectDir)\"
Powershell -ExecutionPolicy bypass -File "$(ProjectDir)WaitForFiles.ps1" "$(ProjectDir)\"

While this is not an ideal solution, it should fix the problem of Visual Studio attempting to publish your application before the required files have been generated by your Gulp tasks. In future versions of Visual Studio I would expect this integration to be a little cleaner.

Ensuring Intellisense for Bower Packages in Visual Studio 2013

February 9, 2015

As I outlined in my previous blog post, I have recently started including client side (CSS / JavaScript) packages via Bower and Gulp instead of Nuget.

One reader asked me how this approach affected Intellisense for the referenced package. He noticed that after using this approach, Visual Studio was no longer giving him the expected Intellisense for Bootstrap CSS classes.

So I set out to investigate this and sure enough I was also seeing the same problem on my machine. I was not getting any CSS intellisense for the Bootstrap classes. This was especially puzzling because it appears that JavaScript intellisense was in-tact.

Let’s dig into this a little deeper then…

JavaScript Intellisense

Intellisense for JavaScript files is handled by the _references.js file. Visual studio handles this nicely with the example in the article. With Autosync enabled by default, the included files are automatically added to the _references.js file. As such, jQuery intellisense works just as it did when referencing the packages from Nuget. Even though our project only includes a couple combined and minified JS files, everything still works exactly as expected.

CSS Intellisense

So what’s up with CSS? Why isn’t CSS Intellisense working? According to Microsoft, VS 2013 is supposed to inspect any CSS included in your project and make classes from those CSS files available via Intellisense in the HTML editor.

Unfortunately Visual Studio seems to ignore any minified CSS files! This seems very strange to me but at least there is an easy solution.  

We can modify our gulp file to also output a non-minified version of the combined CSS file and include that in our project. There is no need to reference this file anywhere. All we need to do is make sure it is included in the project. Suddenly, like magic, everything works again.

image

Here is an updated version of the Styles task from the Gulp file in my previous post:

// Combine and minify css files and output fonts
gulp.task('styles', ['clean-styles', 'bower-restore'], function () {
    gulp.src([config.bootstrapcss, config.appcss])
     .pipe(concat('app.css'))
     .pipe(gulp.dest(config.cssout))
     .pipe(minifyCSS())
     .pipe(concat('app.min.css'))
     .pipe(gulp.dest(config.cssout));
 
    gulp.src(config.boostrapfonts)
        .pipe(gulp.dest(config.fontsout));
 
});
// Combine and minify css files and output fonts
gulp.task('styles', ['clean-styles', 'bower-restore'], function () {
    gulp.src([config.bootstrapcss, config.appcss])
     .pipe(concat('app.css'))
     .pipe(gulp.dest(config.cssout))
     .pipe(minifyCSS())
     .pipe(concat('app.min.css'))
     .pipe(gulp.dest(config.cssout));

    gulp.src(config.boostrapfonts)
        .pipe(gulp.dest(config.fontsout));

});