Node "Console App" & Debugging TypeScript in VS Code

There is an updated post discussing TypeScript 2.0 which also includes Webpack bundling.

One of the most common utility cases when working with SharePoint is to create a new console app, import the PnP CSOM components, and then perform the actions required. This is a way to test code, make quick one-off updates, or process many sites for a common operation. As we have developed the Patterns and Practices client library I've been looking forward to an opportunity to do something similar directly in node. Once we added node request support in 1.0.3 this became much closer to reality. The next step was setting up a project to run an arbitrary TypeScript program and enable debugging, a process outlined in this post.

Step 1 - Setup the Project

As simple as it sounds the first step is setting up our TypeScript project. You can always add any of the libraries you like, but this will get us started for the example. We also then install the typings we want to give us better type checking and intellisense. Once these setup steps are done you can re-use the application by just updating the source code.

 npm init
npm install gulp gulp-sourcemaps gulp-typescript node-fetch sp-pnp-js typescript typings --save-dev
typings install dt~es6-promise dt~microsoft.ajax dt~promises-a-plus dt~sharepoint dt~whatwg-fetch --global --save

Next create a tsconfig.json file in the root of the project and add the below content and save.

 {
 "compilerOptions": {
   "target": "es5",
   "module": "commonjs",
   "jsx": "react",
   "declaration": false,
   "sourceMap": true,
   "removeComments": true,
   "sortOutput": true
 }
}

Finally we need a gulp file to build and run our program. Create a file named gulpfile.js in the root directory and add the below content.

 var gulp = require("gulp"),
 tsc = require("gulp-typescript"),
 maps = require('gulp-sourcemaps'),
 exec = require("child_process").exec;

gulp.task("build", function () {

 var src = ["src/**/*.ts", "typings/index.d.ts"];

 var project = tsc.createProject("tsconfig.json");
 return gulp.src(src)
   .pipe(maps.init())
   .pipe(tsc(project)).js
   .pipe(maps.write(".", { sourceRoot: "../src" }))
   .pipe(gulp.dest("build"));
});

gulp.task("run", ["build"], function (cb) {

 exec('node build/main.js', function (err, stdout, stderr) {
   console.log(stdout);
   console.log(stderr);
   cb(err);
 });
});

We are first requiring the four libraries we need: gulp, the task runner; gulp-typescript, pipes the TypeScript compiler in gulp; gulp-sourcemaps, captures and writes the source maps we generate during build; and child_process.exec, which is the way we can start process in node. Then we define two gulp tasks, "build" and "run". The build task transpiles our TypeScript files into JavaScript and writes out the source maps - these maps will be critical once we begin debugging. Also note we are including the typings index file so our global references are defined during build, this is a common source of unexpected build errors related to references not being defined. Finally we build all the files in the "src" folder and write the results to the "build" folder - these are just names and you can change them to suit your preference.

The run task is what actually takes our built output and runs it. It does so by invoking exec on the main.js file found in the build folder (we'll create a main.ts source file shortly). The exec operation starts a new program in node from the file specified in the path supplied as the first argument. Once we have our program ready we will be able to type gulp run and execute our code.

Step 2 - Write some Code!

Now that we setup our project we can actually do something. Start by creating a "src" folder in your project and adding a main.ts file to that folder. Then add the below contents to main.ts:

 console.log("Hello world!");

Now in your console type the command "gulp run" and you should see your project get built and "Hello World!" written out to the console. Awesome! Now we can get down to business. Update your main.ts to include the below:

 import pnp from "sp-pnp-js";

pnp.setup({
 nodeClientOptions: {
   clientId: "4d4d3329-1d3d-425c-93fa-e4ae393f8cbb",
   clientSecret: "F9iUC6B4LM7TClLWY5aixJEmGDGpvGsXD3lifX7ogts=",
   siteUrl: "https://{your tenant}.sharepoint.com/sites/dev"
 }
});

pnp.sp.web.select("Title").get().then((r) => console.log(JSON.stringify(r)));

You will need to update the clientId, clientSecret and siteUrl values once you register the add-in permissions in your site. If you have properly registered the permissions you can once again use the "gulp run" command and you should see the title of your web written to the console. You can then begin using your app to perform any tasks you want in your site. Remember you need to ensure you've requested the appropriate permissions on /_layouts/appinv.aspx. One little note, batching is currently broken in node - this has been fixed and will be included in 1.0.4 release coming up soon.

Step 3 - Debugging

Once we can run code it would be fantastic if we could debug it to see what is going on. In Visual Studio Code this means adding a launch.json file. First create a folder named ".vscode" in the root of the project and add a file named "launch.json" with the contents:

 {
 "version": "0.2.0",
 "configurations": [
   {
     "name": "Launch",
     "type": "node",
     "request": "launch",
     "program": "${workspaceRoot}/src/main.ts",
     "stopOnEntry": false,
     "args": [],
     "cwd": "${workspaceRoot}",
     "preLaunchTask": "build",
     "runtimeExecutable": null,
     "runtimeArgs": [
       "--nolazy"
     ],
     "env": {
       "NODE_ENV": "development"
     },
     "externalConsole": false,
     "sourceMaps": true,
     "outDir": "${workspaceRoot}/build"
   }
 ]
}

This file will instruct Visual Studio Code how to launch when you hit F5. In your main.ts file set a break point and hit F5. You should see the breakpoint hit and you can examine locals and add watches on the debug tab accessed on the left hand navigation bar. If your breakpoint is not hit, try restarting Visual Studio Code.

Gotcha: If you continue to have issues hitting your break point or are getting the message "Breakpoint ignored because generated code not found (source map problem?)" - ensure you have correctly set the "sourceRoot" property of the source maps write call found in the gulpfile.js's build task. This needs to be a relative path from your built output to your source files.

Now that you can debug your code and use the Patterns and Practices Core Library from a console app you can rapidly develop and test your code - or perform quick queries and updates to your SharePoint sites. Happy coding!

Download the Sample Project

You can download the starter project as well: NodeConsoleApp. You will need to run the following commands to load the dependencies:

 npm install
typings install

What is the JS Core Component?

The Patterns and Practices JavaScript Core Library was created to help developers by simplifying common operations within SharePoint. This is aligned with helping folks transitioning into client side development in support of the upcoming SharePoint Framework. Currently it contains a fluent API for working with the full SharePoint REST API as well as utility and helper functions. This takes the guess work out of creating REST requests, letting developers focus on the what and less on the how.

"Sharing is Caring"