For some time I’ve been looking for a reliable equivalent to #ifdef DEBUG in a JavaScript app, as there are a number of needs to set up different code structures between the two build targets. This includes switching between Windows.ApplicationModel.Store.CurrentApp and CurrentAppSimulator, using test data vs. live data, using test URIs vs production URIs, using test accounts vs. live accounts, using different tokens for back end services, and so on..

I've seen some solutions that utilize t deterministic for JavaScript, but this isn't deterministic for JavaScript because you can debug a Release build just like a Debug build–all the source code is just there, so it's not like C++ where you lose your symbols. I've also seen hacky methods that check for "Debug" in the package install folder path, but that doesn't work for side-loaded apps (like you might share with testers) and maybe not for remote debugging.

It’s really best to get the target configuration straight from VS and somehow apply it to a project, and I worked out how to do it without a VS extension. See this small DebugRelease sample for a demonstration: http://kraigbrockschmidt.com/?attachment_id=1287

What I did is create two .js files in the project (debug.js and release.js in a js-buildinfo folder) that I exclude from the end package by setting their Package Action to None instead of Content. I then use in MSBuild task to selectively copy one or the other file to a common name in the package (e.g. buildinfo.js).

Here are basic file contents:

debug.js:

(function () {
    "use strict";

    WinJS.Namespace.define("BuildInfo", {
        isDebugBuild: true,
        isReleaseBuild: false,

        config: "Debug",
        currentApp: Windows.ApplicationModel.Store.CurrentAppSimulator

        /*
         * Include debug-only data, service URIs, access tokens, accounts, etc.
         */
    });
})();

 

release.js:

(function () {
    "use strict";


    WinJS.Namespace.define("BuildInfo", {
        isDebugBuild: false,
        isReleaseBuild: true,

        config: "Release",
        currentApp: Windows.ApplicationModel.Store.CurrentApp

        /*
         * Include release-only data, service URIs, access tokens, accounts, etc.
         */
    });
})();

To do the selective copy, it’s necessary to add a BeforeBuild action in the project file. At present, VS doesn’t allow custom build configuration for JS projects through the UI, so you have to do the following:

  • Right click and Unload Project in VS.
  • Right click and Edit the project manually.
  • Make the changes below.
  • Right click and Reload Project.

Editing the .jsproj file I added the following entries under the ItemGroup with the project files:

<ItemGrup>
  <BuildFlagSource Include="js-buildinfo $(Configuration).js" />
</ItemGroup>
<ItemGroup>
  <BuildFlagDestination Include="jsbuildinfo.js" />
</ItemGroup>

And then farther down there’s a section that’s commented—you uncomment it and add the <Copy> element shown here:

<Target Name="BeforeBuild">
  <Copy SourceFiles="@(BuildFlagSource)" DestinationFiles="@(BuildFlagDestination)" OverwriteReadOnlyFiles="true" SkipUnchangedFiles="true" />
</Target>
<Target Name="AfterBuild">
</Target>
<PropertyGroup>
  <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
</PropertyGroup>

Ideally you’d make buildinfo.js read-only in the project to prevent editing what will be overwritten in the build.

And the you can just have this line in whatever HTML files need it (usually before other .js files that would use the BuildInfo properties):

<script src="/js/buildinfo.js"></script>

Things I like about this solution:

  • It’s extensible as you can add anything you want to the debug.js and release.js files.
  • The BuildInfo namespace can include methods to do build-specific work, which is sometimes necessary.
  • It enabled isolation of all build-specific code in these files, rather than littering it throughout the rest of the app as you would with just a simple flag. I still have a flag in here as an option, but you wouldn’t have to use that at all.
  • It works no matter how an app is deployed.
  • It’s not dependent on any compiled VS extension that you’d have to produce for x86, x64, and ARM.

A few downsides:

  • Have to hand-edit a project and do some one-time configuration like adding a buildinfo.js and making it read-only.
  • You have to make sure that the debug.js and release.js files define the same stuff.
  • It feels a bit fragile given the manual steps.

What do you think of this solution? It works well and reliably applies the chosen build target to your JS code. But are there things I've missed or things that can be improved?


I ran into this alongside the findAllAsync trouble I was having in the previous post. To be more specific, I've been converting a small sample that's in the ZIP file for //build 2013 session 3-026 (http://channel9.msdn.com/Events/Build/2013/3-026, the ZIP is http://files.channel9.msdn.com/thumbnail/f9025b07-120c-4320-8de6-a5de2d255b7c.zip) . This is a C# sample to talk to a Sphero device using the Windows 8.1 Bluetooth.Rfcomm API.

The problem I was having was that write.storeAsync (write is a Windows.Storage.Streams.DataWriter object) was throwing an exception, hence my try/catch block:

function changeColor() {
    output.innerText = "";

    if (writer == null) {
        return;
    }

    var packet = generateRandomColorPacket();
    writer.writeBytes(packet);

    try { 
        writer.storeAsync().done(function () {
        }, function (e) {
            output.innerText = "Failed to write packet; " + e;
        });
    } catch (e) {
        output.innerText = "DataWriter.storeAsync failed (exception); " + e;
    }
}

The exception message wasn't at all helpful: it just said: "The operation identifier is not valid." Huh?

With the help of some colleagues in the Bluetooth team who looked this over, we found a small glitch in the code below that opens the socket to the device. Do you see the error?

function openSocket() {
    output.innerText = "";

    if (device == null) {
        return;
    }

    // Create a socket and connect to the target
    var sockNS = Windows.Networking.Sockets;
    socket = new sockNS.StreamSocket();
    socket.connectAsync(device.connectionHostName, device.connectionServiceName,
        sockNS.SocketProtectionLevel.bluetoothEncryptionAllowNullAuthentication)
        .done(function () {
            writer = new Windows.Storage.Streams.DataWriter(socket.OutputStream);
        }, function (e) {
            output.innerText = "Failed to open socket; " + e;
        });
}

Don't feel bad if you missed it, because I did. It's this line here, which I'd brought over from the C# code. Now do you see the problem?

writer = new Windows.Storage.Streams.DataWriter(socket.OutputStream);

It's hard to see: socket.OutputStream should be socket.outputStream! In other words, I was creating a DataWriter with undefined rather than the real stream. DataWriter didn't complain, though, until it tried to access the backing stream via storeAsync.

Changing that one character solved the problem.


While working on the Devices and Printing chapter of the second edition of Programming Windows Store Apps with HTML, CSS, and JavaScript, I’ve been bitten several times by a subtly confusing feature of the device enumeration API and it’s projection into JavaScript.

Device enumeration happens in a few different ways, but a common method is to ask a device object like HidDevice or Bluetooth.RfcommDeviceService (both under the Windows.Devices namespace) for a “selector” string through a static method called getDeviceSelector. You then pass this to the Windows.Devices.Enumeration.DeviceInformation.findAllAsync.

The gotcha is that findAllAsync has two overloads that take one argument: one that takes a selector string, and one that takes a DeviceClass object. Only one of these, however, is projected into JavaScript and it’s the DeviceClass variant. Unfortunately, if you happen to pass a selector string to this method, you’ll get no complaint anywhere along the way: the API will accept the string, see that it’s an invalid DeviceClass, and then ignore it.

As a result, this variant of findAllAsync just assumes then that you didn’t need the argument and does the equivalent of findAllAsync() with no arguments, which enumerates every device on your system (all 300+ of them, typically!). This is, like I said, very confusing if you were only expecting to see a single result.

When using a selector to enumerate devices in JavaScript, then, be sure to call the findAllAsync(<selector>, <properties>) variant, passing null for <properties>. This ensures that you use the findAllAsync that expects a selector string, and thus you’ll get the results you’re looking for.