Developer Drain Brain

April 12, 2012

JSLint with msbuild

Filed under: Development — Tags: , , , , — rcomian @ 10:17 am

Recently, I’ve been working with Javascript for a project at work and want to get all the files linted with JSLint.

Since we’re still a Microsoft shop (how I hate that phrase) and intend to build things with msbuild, it makes sense to get this working under msbuild.

Now so far, I’d had jslint running from the windows version of node from the commandline. Works like a demon, no problems. I’ve also had lint working from a makefile as well. So plugging this into msbuild … how hard could it be?

Well, msbuild includes the wonderful “Exec” task, for running programs during a build. So I thought I’d stick with what I knew and run jslint from there.

Now on the commandline, you can run jslint and it picks it up from the path (and runs it under node automatically). Not so with msbuild. Of course, you can’t reference the jslint.js file directly either, since it’s not an executable. So I had to go with “node ../jslint.js — files to lint”.

No problem, this is fine. And it works too. When the files all lint properly, it’s exactly as you’d want. When there’s an error, however, you just get “error”. The build fails, but the error messages from jslint are suppressed. I think you’d call this “minimal functionality”.

So how to get the error messages visible? First I tried building with /v:diag, just to make sure that they weren’t there at all – they weren’t. Second I tried redirecting stderr->stdout, as I’d read somewhere that the Exec task doesn’t capture stderr. I used various incantations of “2>&1” and “1<&2" depending on who's writing the article and whether you're thinking unix or dos.

Still no dice.

Next I'm starting to think custom actions. Now, I've been playing in Javascript for a while now, and suddenly I'm looking at C# and thinking – seriously? I've got to write the code, then compile it myself, then manually take care of the resulting .dll file and use that? Compiled code can be a real drag. I know, I've spent the last 10 years working with it.

Fortunately, msbuild now contains "inline tasks“, which fit the bill quite nicely. I can now write a quick little task and reference it.
So I did, just a simple process exec set up to run node with the appropriate command line. It all worked fine too. In exactly the same way as the “Exec” task.

Even though I was manually reading both streams and logging both outputs, jslint just doesn’t print the error logs when running from msbuild. It does print the name of the file that fails, so it’s getting output, but that’s all.

Now this isn’t 100%, I have seen the correct failing output on some occasions, even from the Exec task. So it’s something screwy going on with node and jslint. But we’re a microsoft shop, we don’t even use node, so it’s not really worth my time debugging this too much.

I started looking around for alternatives. I did find the Sedo Dream collection of tasks, which includes an jslint build task, but I really want to have a “check this out, build it” workflow, and installing a 3rd party msi just doesn’t cut it. Sometimes there’s no alternative, but I don’t want to add something unless it’s really necessary. There’s no equivalent zip file for this, so I’d have to repackage it myself for distribution, and it looks like quite a large library of “yet another collection of generic msbuild tasks” that is no-doubt wonderful, but we’ve got a lot already.

Finally I came across JSLint for WSH. This looked promising, since it was a single javascript file running in the normal windows environment. It was great.
One of the things I look for with this kind of wrapper is “how do you update it”? I noticed that the last checkin of the jslint part of the package was from August 2011. That’s a little out of date, I know old Crockford updates more often than that.

Looking at the source, I realised that it was simply the core of JSLint with a small executor and reporter tagged onto the end. I pasted the latest version of JSLint into the top of the file and it worked fine, so I’m quite happy that it’s easy enough to keep it up to date, even if the package authors haven’t felt the need to.

But it still wasn’t quite right. First off, it completely balked at my actual javascript files. Turns out they’re unicode with byte order marks(BOM), which JSLint was trying to read as javascript. Now the node version of jslint worked fine, so I looked at what that was doing – and it was as simple as checking the first three characters, seeing if they were the BOM and removing them if they were.
Pasting this code into jslint-for-wsh didn’t work immediately. The original code was using a compare like this:

if (0xEF === content.charAt(0) ...

Whilst it works fine on node, I don’t blame cscript for having trouble with this. Changing it to get the actual character code (ie. a number) to compare against a number works fine:

if (0xEF === content.charCodeAt(0) ...

Finally, I found that all my line numbers were off. After a little head scratching, I realised that it wasn’t counting blank lines. It turns out that cscripts split function ignores blank lines, meaning that JSLint has no way of keeping track or where it actually is in the code.

The way around this was to do a little more work and build up the array of strings as we read the file, rather than read the file as one big text blob and make jslint split it up. It’s a simple while loop and ReadLine rather than just ReadAllText.

To run all this, a simple Exec task works fine:

<Target Name="jslint" Condition="'$(BuildCmd)'!='Clean'">
<!-- Define the files to lint -->
<ItemGroup>
<FilesToLint Include="*.js" />
</ItemGroup>

<Exec Command="cscript //B //NoLogo //T:10 &quot;..\JSLint\jslint-for-wsh.js&quot; &quot;%(FilesToLint.FullPath)&quot;" />
</Target>

And now we have fully linted code at every build.

Take care

Advertisements

Blog at WordPress.com.