Thursday, July 28, 2016

Expose an ES2015 Module as a Global with Browserify (react-addons-update)

Calling the React setState() method within TypeScript in Visual Studio Code requires that all properties of the state object are set in the parameter to that method. That was proving a nuisance when there are a few state properties, so I decided to use the update addon available in React - that allows for a much simpler command such as
let newState = update(this.state, {
    message: {$set: "Success!"}
});
this.setState(newState);
Adding the react-addons-update module without any other changes to the build operations in gulp caused the content of that addon to be included in the bundled JS when processed by browserify.

That additional content can be excluded from the bundled JS by these adjustments:
  1. Use the "external()" method on browserify to remove it from the bundle
        let bundler =  browserify({
                entries: config.rootJS,
                debug: true //This provides sourcemapping
            })  //Initialising browserify
            .external(['react', 'react-addons-update', 'react-dom', 'react-router']);
    
  2. Add a shim entry in the package.json file to map a global name to the addon
      "browserify-shim": {
        "react": "global:React",
        "react-addons-update": "global:React_Addons_Update",
        "react-dom": "global:ReactDOM",
        "react-router": "global:ReactRouter"
      }
    
  3. Create an additional JavaScript file containing the ES2015 addon code transpiled to E5
That last stage required a new gulp task, passing the file "update.js" file from the React source to browserify.

However, that new bundled file for the addon did not expose a global name that could be used within my custom bundle. The key to exposing this global name proved to be adding the "standalone" parameter in the browserify call (line 6 in the code below).
gulp.task('packageReactAddons', () => {

    let bundler =  browserify({
            entries: 'node_modules/react/lib/update.js',
            debug: false,
            standalone: 'React_Addons_Update'
        });

    return bundler.bundle()
        .on('error', console.error.bind(console))
        .pipe(source('react_addons_update.js')) // Pass desired output file name to vinyl-source-stream
        .pipe(gulp.dest(config.librariesOutputPath)); // Destination for the bundle
})
After uploading the resulting JS file to a page, I could see the object named "React_Addons_Update" in the global name space of the page. Yeah!

Tuesday, July 26, 2016

Using a JavaScript Library in TypeScript Modules - the D.TS FIle

Update - After restructuring the JavaScript code to separate views from components, as per the great article by Brad Westfall, I found that the local custom declaration file no longer satisfied the compiler. The reason is that the views and container components are placed in separate folders, which breaks the relative module path references. For the sake of expediency, I have resorted to moving the custom declaration file into the SPScript node_module folder. Not great, I know, but it works and so will do in the meantime.

If you are needing to hand-craft your own d.ts files, have a look at Complex Typescript Definitions Made Easy by Steve Fenton. You'll be glad you did!
The JavaScript library SPScript simplifies REST calls to SharePoint from the client by wrapping the AJAX requests in higher level abstractions. Seemed a useful library for my trials with TypeScript, React and D3 in presenting SharePoint data, so I added it to my source code in Visual Studio Code, and imported the file into my module App.ts.

Issue - Cannot Find Module SPScript

Its not that simple in Typescript. First issue to correct is the red squiggly that appeared under SPScript in the following statement:
import SPScript from "SPScript";
This error notification from the editor environment indicates that TypeScript cannot locate a module that matches that import statement. Looking through the module resolution notes in the online TypeScript handbook shows the locations and files which the compiler will check for the sought module (see the section titled "How TypeScript resolves modules" on that page for the details). SPScript is provided as a plain JavaScript library, without .TS or .D.TS files. So none of the files expected by the node name resolution would be found.

To correct this, it was necessary to add a file at one of those locations. Rather than make any adjustments to the SPScript node module (risky!), I added the declaration file SPScript.d.ts in the same folder as the App.ts file, and added an export statement to that new file to set this file as a module.

A note on Visual Studio Code - I have noticed that VS Code is sometimes slow to indicate that errors are corrected. In this case, the red squiggly did not disappear, even with the new file in place. If you close and reopen the file with the issue (in my case, App.ts) then the error was gone when the file reopened!

Issue - What to Include in the Declaration File?

The call I wanted to make using SPScript is as follows:
    const dao = new SPScript.RestDao(_spPageContextInfo.webAbsoluteUrl);
For TypeScript to recognize the types in those statements, the declaration file SPScript.d.ts needs to define the various classes and parameters. I tried various combinations of statements in the declaration file, eventually using this:
export module SPScript {
    export class RestDao {
        constructor(webUrl: string);
    }
}
This approach allowed TypeScript to resolve the types, and the compiler successfully created a JS file.

So, all ready to go? Nope - looking at the code in the compiled JS file, that statement had become
    var SPScript_1 = (typeof window !== "undefined" ? window['SPScript'] : typeof global !== "undefined" ? global['SPScript'] : null);
    //... other statements
    var dao1 = new SPScript_1.SPScript.RestDao(_spPageContextInfo.webAbsoluteUrl);
That would fail in the browser, as the inclusion of the library SPScript.JS in the HTML page gives access to SPScript.RestDao not to SPScript.SPScript.RestDao.

Plenty of investigation, trial and error lead to the use of this in the declaration file:
export class RestDao {
    constructor(webUrl: string);
}
The import statement was adjusted to
import { RestDao } from "./SPScript";
And the successful outcome of the compile process gives this in the JavaScript file:
    var SPScript_1 = (typeof window !== "undefined" ? window['SPScript'] : typeof global !== "undefined" ? global['SPScript'] : null);
    //... other statements
    var dao = new SPScript_1.RestDao(_spPageContextInfo.webAbsoluteUrl);

A note on the Global Name and Browserify-Shim

The ES5 JavaScript file is being created from the TypeScript modules with the help of Browserify within a Gulp task
gulp.task('package', ['includeLibs', 'compile'], () => {
    let bundler =  browserify({
            entries: config.rootJS,
            debug: true //This provides sourcemapping
        })
        .external(['./SPScript']); 

    bundler.bundle() //start buindling
        .on('error', console.error.bind(console))
        .pipe(exorcist(config.distOutputPath + '/' + config.bundleFile + '.map')) //Extract the sourcemap to a separate file
        .pipe(source(config.bundleFile)) // Pass desired output file name to vinyl-source-stream
        .pipe(gulp.dest(config.distOutputPath)); // Destination for the bundle
})
Notice line 6 - this specifies that the SPScript is not to be included in any way in the output JavaScript. In addition, the global SPScript name needs to be made available within the bundled output modules. This is achieved by including the browserify-shim module in the source code, and adding the following to the package.json file:
  "browserify": {
    "transform": [
      "browserify-shim"
    ]
  },
  "browserify-shim": {
    "./SPScript": "global:SPScript"
  }
Note that the exact text used in the import statement - in this case it was ./SPScript - must be passed to browserify-shim, so that the correct reference occurs within the compiled JavaScript.

Sunday, July 24, 2016

Modern JavaScript in SharePoint (4) - Gulp and PnP

PnP is the Office 365 Developer Patterns and Practices program - a (very) comprehensive resource illustrating techniques for coding against SharePoint and Office 365. Amongst the many offerings is the JavaScript Core Library. This library wraps the calls to the SharePoint REST API in a set of objects (or, to be accurate, in a series of namespaces and functions that mimic an object collection - this is ES5 JavaScript, after all!). The addition of this layer of abstraction simplifies many of the operations that are possible from client-side code.

For example, submitting a search against SharePoint becomes as simple in JavaScript as this:
pnp.sp.search({ Querytext: "Document", RowLimit: 5 })
 .then(function(data) { console.log(JSON.stringify(data)); })
 .catch(function(e) { console.log("Error: " + e.message); });
The calls in the library handle the asynchronous nature of the interactions - hence the "then" and catch" functions for processing the retrieved data or any error that occurs.

How can this library be incorporated into a build workflow using ES6/ES2015 JavaScript in Visual Studio Code? In my trials, I wanted to integrate the build and deployment process using gulp, so first I investigated options for upload files to SharePoint Online within a gulp task. And happily there is a gulp plugin ready to achieve that - its called gulp-spsave. It is very simple to use, with the only disadvantage being that the credentials used to connect to Office 365 are supplied as plain text within the call. An alternative is to use gulp-spsync by Wictor Wilen - but for the purposes of these tests I have stuck with the spsave plugin.

This allows the upload of files into the site assets library in SharePoint to be achieved by:
gulp.task('upload-sp', function () {
    return gulp.src(config.bundleFilePath)
        .pipe(print())
        .pipe(spsave({
            username: settings.username,
            password: settings.password,
            siteUrl: settings.siteUrl,
            folder: "SiteAssets",
            notification: true
        }));
})
In this task, the account details and the target site are stored in a separate settings JSON file. I also have a separate similar task to upload libraries, such as PnP and any other dependencies.

Compiling PnP in Typescript in Visual Studio Code

So, now to use PnP in JavaScript in Visual Studio Code. First step is to add the module into the code's folder structure using the npm call on the command line (in the useful integrated terminal window):
npm install --save-dev sp-pnp-js
This adds a folder into the node_modules directory tree that includes the pnp.js library.

Next, the PnP library needs to be imported into the JavaScript module in which calls will be made using the library. In ES6/ES2015 this is achieved by the line
import * as pnp from "sp-pnp-js";
That seemed simple. But its not quite that easy. When I added a PnP call in my code in Typescript, and attempted to compile it using the Typescript compiler, I received various errors. Visual Studio Code started giving several naming errors. The TypeScript compiler was looking into PnP.js and finding lots of names it did not recognize.

At run time in the browser, these names would be available in the global namespace as the PnP library is built knowing that various other JS libraries are always referenced from SharePoint pages. But the point of using strongly typed JavaScript is to check its validity at compile time, so the compiler needs to know about all names used in all the current scripts. So I needed to supply the compiler with references for all names used in PnP.js. Experimentation lead to the following inclusions for overcoming the compile errors:
  • PnP is dependent on ES6 Promise and on whatwg fetch at runtime, so the type definitions for these libraries are needed. As an example, a compile error may complain of "promise" being an unknown name. These definitions can be added to the source code in VS Code via the following statements which get the necessary files from the DefinitelyTyped online resource:
    typings install dt~es6-promise --global --save
    typings install dt~whatwg-fetch --global --save
  • PnP also makes considerable use of the Microsoft Ajax library. I could not find a command line statement with which to add this type, so ended up manually getting the Microsoft-ajax.d.ts file from a GitHub location. I then manually added a sub-folder under the typings globals folder structure, and included a reference to this in the index.d.ts file under typings.
  • Last name correction was to add SharePoint-specific name declarations. For these, I found a file SharePoint.d.ts which I also manually added to the typings in this source.
This took a while, but finally managed to get the Typescript compiler to accept PnP!

Bundling The JavaScript for Deployment

Once the code could be compiled into an ES5 file, the ES5 then was ready for use on a test SharePoint page. Or was it? Looking at the file created using browserify in a gulp task, it was a huge file, as it included all of the PnP library. Clearly this is not what we want (as the PnP.js minimized library would be referenced from the page), so needed to tell browserify to exclude that library. This is done using the "external()" command:
    return browserify({
            entries: config.sourceJS + 'spTests.js',
            debug: true //This provides sourcemapping
        })
        .external(['sp-pnp-js'])
        .bundle()
        .on('error', console.error.bind(console))
        .pipe(source(config.bundleFile)) // Define the name of the bundle
        .pipe(gulp.dest(config.tsOutputPath)); // Destination for the bundle
In this gulp task, the compiled file "sptests.js" is being prepared for upload - the reason I am taking this approach is to be able bundle multiple modules into a single file. The second command in the pipeline tells browserify not to include the pnp.js library in the bundled output. Great, the completed file is now much smaller.

Using the file in SharePoint

The bundled file is uploaded to SharePoint using gulp-spsave, along with the libraries on which it is dependent. It nearly ran in the test page, but showed an error - the name "pnp" was not recognized in the script available on the page. Looking at the PnP library, it exposes "$pnp" rather than "pnp". So the gulp process needed to somehow map all calls to pnp to actually use $pnp.
browserify-sim to the rescue. Adding the following to the package.json file, and running the gulp tasks again, gave a bundled ES5 file that successfully uses PnP calls!
  "browserify": {
    "transform": [
      "browserify-shim"
    ]
  },
  "browserify-shim": {
    "sp-pnp-js": "global:$pnp"
  }

Thoughts on the Process

As you can see, writing code in Visual Studio Code in Typescript against PnP and getting that code to run in a SharePoint page is at the moment a mission. I am sure it will get easier.... If you need any tips about the concepts here, please drop leave a comment & I'll be in touch.

Thursday, July 21, 2016

Reflections on Learning React....

What's quiet amusing (and frustrating….) is that the simplest user interactions with page elements have to be relearnt in a way fitting the paradigms of the React model. As an example, imagine a search form. Just a text box and a button, simple right? In "classic" JS, adding an onclick event handler to read and search on the value in the textbox takes no thought at all (ignoring here the "production -quality" trimmings that are necessary in a real world form, such as validation).

Reproducing this interaction in React requires knowledge of a few principles:
  • Event handlers are attached to an element as a property. If using JSX, be careful not to enclose the handler in quotes!
     < input onclick={this.submitSearch} type="button" value="Search" />
    
  • The "this" context pointing to the React component is not available in the event handler. That means that if the value in the text box is bound to a member of the component state, this value would not be accessible in the handler. There are two approaches to correcting this - either add the "bind(this)" call to the end of the handler invocation
     < input onclick={this.submitSearch.bind(this)} type="button" value="Search" /> 
    or define the handler as an arrow function
    submitSearch = ( e ) => { … }
  • Bind the text box default value to an initial value. Do not bind the value property, as doing so causes the box to behave as read only in the form. Also need to add an onchange handler for the text box, bound to a function that updates the appropriate member of the state object with the value from the control (e.target.value)
  • Add the shouldComponentUpdate() lifecycle function to the component, ensuring that the change of text value in the state member does not trigger the update (unless you want the search to occur on every key stroke)
  • Setting the state in React (using Typescript in VS Code) requires all members of the state object to be set each time

Tuesday, July 19, 2016

Browserify is Sensitive in its Entry Files Parameter - "Cannot Find Module" Error

Faced an issue today whilst using Browserify to bundle some JavaScript files. The JS files had been compiled in Visual Studio Code from Typescript TSX files via gulp, and were all stored together in a single intermediate folder.

The single JS output bundle file was created just fine by Browserify. But when used in an HTML page, that file would result in a "cannot fund module" error. This error is created in the _prelude.js module, part of the output from Browserify.

So, it was time to explore the contents of the bundled JavaScript file. That file contains sections for each of the bundled modules. The last of those modules is generally the one that executes after loading. If that module needs to reference methods or properties exported by other modules, then there should be a mapping between the exported module name and that module key in the bundled modules array. Looking at the end of my output file, that mapping was as follows:

},{"./AppHeader":undefined}]},{},[1,2,3,4])

This means that the "AppHeader" module was not reachable, leading to that "cannot find module" error. After plenty of research into the bundling tool (during which I came across this useful article by Ben Clinkinbeard that describes the Browserify actions), I went back to look at the calling task in gulp. There I found that the Entry Files parameter was using a glob to point at all the JS files in the intermediate folder - the result of some previous testing in a project on which this set of files was based.

I changed this parameter to point just at the single root JS file, reran the bundling task in gulp, and now the output file ended with this:
},{"./AppHeader":1}]},{},[2])

This new bundled file worked just fine in the browser. So beware the Entry Files parameter!

PS For lots more details on this tool, consult the Browserify handbook!

Modern JS in SharePoint (3) - NPM and Gulp

Here's a quick analogy to help visualize the difference between using Visual Studio and Visual Studio Code. Imagine you need to build a bookshelf. You can pop into Ikea, select a few shelf unit to suit your needs, add a few accessories, and then spend an hour or two at home assembling the units. All the instructions and the fittings are in the kit.

Alternatively you can build from scratch, selecting the wood, learning to use the right tools for each task, choosing the hardware from the huge available range of screws & bolts & brackets. Along the way you gain skills with the tools, and create something totally customized for your needs.

No doubt you see where this is leading.... Starting our with VS Code feels a little like an apprenticeship, with so many new techniques and concepts to master. The familiar development workflow in Visual Studio of "New Project..." is replaced when using VS Code by decisions of what tools and facilities I need in this project, and what build steps will be appropriate.

I think this is a good learning process to face. Disrupting our normal routines can be painful at first, but helps make us aware of new and better ways to do things. Core to the new development workflow is npm, the Node Package Manager. This is usually compared to Nuget, but npm is a more vital element in the process than Nuget is in VS.

There is plenty of guidance on the web of how to install Node and npm, so I won't repeat it here (in case you were wondering, Node runs JavaScript code in a background process, and is required if using npm & gulp).  I will, however, mention the usefulness of the integrated terminal window in VS Code (CTRL+`) - that has been the place in which I have carried out all the command-line work necessary in my projects.

Starting a New Development Project

Visual Studio Code focusses on files within a folder structure. The tools used within the editor expect files with particular names to be in certain locations in that folder structure - convention over configuration. The general principle when starting from scratch is to create a top level folder for you new development files, open that folder in VS Code, and then run the following:
npm init -y
This creates the package.json file, similar in some ways to the project file in VS - it stores high-level settings for your development project, including the node modules that are used during the development operations.

Tailoring the Development Workflow

Node modules are plugins that can add content to your development project (such as libraries needed in your code). And node modules can also add task handlers to help with the development workflow. We are working here in the open source world, so there are a variety of "competing" options when choosing how to automate and orchestrate tasks in your development process. By this, I mean the ability for the development environment to do such things as check the quality of our source code("linting"), running tests, translate coding languages into runnable formats (compiling or "transpiling"), and packaging the source code for deployment.

The main players at the time of writing for creating workflows from these tasks (otherwise known as task runners) are gulp and grunt. An alternative is to use webpack which is a bundling tool that is able to run tasks other than just bundling files. Each takes a different approach to automating tasks. Each can suit different projects, or can seem a better fit for our own way of working. Gulp defines the operations to perform in JavaScript, whereas grunt (and webpack) use configuration files to define the operations.

Using gulp

After some experimentation with webpack and gulp, personally I prefer gulp. It gives me huge flexibility in how I organise the development tasks, is easier to debug through the use of logging statements to explore status of the tasks, and I find it easier to visualize thanks to the low-level step-by-step control. But you may prefer one of the other approaches - as I say, its down to personal preference, as you can probably achieve the same outcomes with any of the approaches. James Nelson provides some useful ideas on this subject.

If you choose to use gulp, it is worthy to know that the gulpfile can be written in ES6/ES2015 - this article by Mark Goodyear explains how to achieve this (using babel, and renaming the gulpfile). Why do this? Its another opportunity to become familiar with the new coding styles in ES6/ES2015

In the next installment, I will explain the gulp configuration I have found useful.

Monday, July 18, 2016

Modern JS in SharePoint (2) - Visual Studio Code

How to compare the Visual Studio full IDE with Visual Studio Code? If the VS IDE were a high spec campervan, replete with every mod con and comfort to offer good living anywhere, VS Code is the nippy little two door sports car. Pared-down for speed and performance, but with enough optional extras to allow for comfort as well as fun (so, more Porsche than Mazda!).

SharePoint development used to need access to the server-side objects, allowing the compiled code to interact directly with the farm. In the newer world of building at the front end, that tight coupling is replaced by remote hosted compiled apps or by client-side code. So Visual Studio no longer needs to run on a SharePoint server.

The lightness of Visual Studio Code is refreshing. It loads quickly on my laptop, and it views my work as files in a series of folders rather than as solutions or projects. Extensions are available in a Marketplace, but for now I am keeping my install lean - after spending time recently investigating a problem in Visual Studio Professional related to a troublesome extension, I am keen to reduce the potential for add-ins to cause me difficulties. The marketplace includes extensions to aid the development workflow, such as tools to check the quality of your code (linters and validators) - but I suggest ignoring these for now. Instead, have a go with the task runners and associated tools from the world of Node. One of the huge benefits is that you'll gain web dev experience applicable beyond the SharePoint "walled garden".

I challenge you to look through the list of extensions you have installed in Visual Studio, and think about which of those you actually use? I know the feeling... "that looks useful, I'll install it and might use it later..."!

Learn the VS Code shortcut keys, and you can quickly be cruising along concentrating on writing good code rather than on nurturing the development environment. The integrated terminal window will soon become very useful in this new world of npm and task runners, so CTRL+` is a good combination to learn straight away.

You can grab VS Code from here - last time I downloaded the setup package, it was about 60MB in size.

Sunday, July 17, 2016

Modern JS in SharePoint - the Motivation

When building customisations and enhancements for SharePoint, JavaScript has been my technology of choice for the last few years. It simplifies the life cycle management, works across SharePoint Online or on-premises, and offers a huge foundation of libraries upon which to build. It also is being acknowledged as the way forwards within the SharePoint product itself - the "Modern Document Library" released in Office 365 uses the React JS framework.

I have to admit, though, that my JavaScript was being created in the "old fashioned" way. No ES6, no build process, just the "quick" approach of a simple file in the style library within SharePoint. That approach brings the benefits of simplicity in structure, of being quickly updateable where needed, and of requiring just a text editor plus the dev tools in the browser. It is a very effective technique for small enhancements such as modifying the display of a column in a list view.

But those benefits are eclipsed by the downsides when building bigger applications in JavaScript. Very quickly a single JS file becomes too long. Encapsulating methods and properties in IIFEs helps to some extent, but refactoring and testing becomes hard. The code becomes sensitive to change. A better way is needed.

One such "better way" is to adopt the tools and practices that are commonplace in the open-source web dev world. Amongst these are the use of:
  • Coding in modern JavaScript - ES6 and/or Typescript
  • "Transpiling" that code into JavaScript that is compatible with current browsers (as the browsers are not keeping up with all features in the standards) - Babel
  • Packaging the code to improve download times and browser processing
  • Configuring flexible build processes through the creation of task runners - Gulp
  • Manipulating code in nimble code editors rather than heavy-weight development environments - Visual Studio Code
It's a lot of change! I dived into that world last week, and getting to the first speck of working code in a SharePoint page has been a challenge, for sure. Here I would like to document some of the things I have tried, and the lessons I have learnt. The combination of tools in my new working set are not necessarily the right fit for your circumstances. I am not suggesting that this combination of tools and techniques are recommended as being best of breed - they are the ones that are working for me at present. My aim in this small set of articles is to give a little help if you are interested in the evolution to "enterprise-scale" front-end development for SharePoint.