Article
0 comment

Use custom gulp tasks in the new SharePoint Framework

This was actually the first question I asked after the new framework has been released. Since then there has been an ongoing discussion on that issue.
When you created a new project using a yeoman generator you’d expect a proper gulp/grunt/whatsoever file that list all the task required to build and develop the project.
When you open the gulp file of the new SharePoint Framework you see just the following lines of code.

'use strict';

const gulp = require('gulp'),
build = require('@microsoft/sp-build-web');

build.initialize(gulp);

The rest of the SharePoint framework is well hidden and deeply nested inside the node_modules folders. Theoretically, you can whatever you like in this folder, but your changes will get lost whenever fresh version will be checkout out form the source control and/or npm install will be exited, upgrade your project to the newest drop of the SharePoint Framework or install an updated version of any package. The node_modules folder is the _layouts folder of the new SharePoint Framework but you can be sure that files in there will be always replaced.
My mate Waldek wrote a great blog post on how to extend the SharePoint Framework with a custom build task.
I think his article is suitable for a deep integration in the SharePoint Framework. From my point of view, it solves a problem that exists because of the Framework.
I working with yeoman generators for more than two years now and I’ve never seen a gulp implementation that only contains of a simple function call. The new SharePoint Framework follows in this case a pretty uncommon approach. I was clueless for a while.
In SPFX everything is built on gulp and it turn’s out that adding a custom gulp task is much simpler than I have expected. However, sometimes it is hard to see the forest for the trees.
Let me explain how to accomplish the same thing Waldek describe just by standard gulp methods but first let me explain some basics.

Gulp automation, task and dependencies

Gulp is a so called task runner that supports your development and allows to automate various build and development tasks while you are coding. The following example shows three basic gulp tasks that just logs messages on the console.

gulp.task('Hello', function() {
    console.log('Welcome');
});

gulp.task('SPFX', function() {
    console.log('to the new SharePoint Framework');
});

gulp.task('HelloSPFX', ['Hello', 'SPFX'], function() {
    console.log('Have fun using it!!!');
});

The first Hello and second task SPFX are just two simple tasks. The first parameter is the name of this task followed by the function that contains everything that should be executed when that task will be executed.
The third gulp task is different and use three parameters instead of two. Again, the first parameter is the name HelloSPFX, the second parameter are now dependencies and the third parameter is again the action to take. The newly added dependencies define that task Hello and task SPFX need to run before the HelloSPFX will be executed.
That’s the core magic of gulp. Define and an task, assign an action and combine them to build flexible build chains.
May Hello be your TypeScript compilation task, SPFX your JavaScript minification task and HellpSPFX copy your files to another location in your project. A folder that contains everything you need to deploy for example.

gulp-cli – the client that executes your task

To execute a task defined in the ‘gulpfile.js’ a tool named gulp client (gulp-cli) will be used. When you copy the code above in the gulp file of your SPFX project you would expect the following results.

SPFX - Expected output

SPFX – Expected output

Instead the default build task of the SharePoint Framework will be started. In addition your task will be executed too. You see the real result in the screenshot below.

SPFX - Unexpected output of custom task

SPFX – Unexpected output of custom task

 

From my point of view, it is an architectural bug and is caused by the following line in the gulp file:

build.initialize(gulp);

This is simple line is the root cause and the problem. Whenever you add something to the ‘gulpfile.js’ that should be executed individually you will first start a release build and in addition, your custom task will be executed. Comment this line out and you are fine; uncomment it when you like to launch the workbench again. As you see its not a convenient way to use custom tasks this way.
Let’s integrate a custom task that watches for changes in the file system of your project while you are developing.

Integrate a simple file watch into SPFX

First, I created an addition folder in the that root of the project. I named the folder ‘myfiles’. The I created a simple task that registers an event receiver on this folder. Similar to a SharePoint list item event receiver.

var handleFS = function(obj) {
    console.log(obj);
}

gulp.task('filewatch', function() {

    var watchTarget = 'myfiles/**/*';

    gulp.watch(watchTarget, handleFS);

});

The watchTarget variable defines the path that should be monitored and gulp.watch register the event receiver on the defined folder.
Whenever you change a file inside the ‘myfiles’ folder the handleFS function will be executed. The function itself is rudimentary and again just logs the event data to the console.
This simple watch is persistent as long as the task is running ever file change will be monitored and the event data will be logged. Kill the task and the event receiver will be removed from the folder.

The problem

When the filewatch task will be called from the command line using gulp filewatch a new release build will be executed. The file watch task will be executed too, but stops working after SPFX finished its work. Our task will be killed by the release build.
gulp serve can be executed too but build.initialize(gulp); again will only watch for file style changes monitored by SPFX. All other tasks in the gulp file will be ignored.

The solution

This problem can be solved easily. As mentioned before. Whenever you execute gulp on the command line you actually not execute gulp. You tell the gulp client (gulp-cli) which task to start. The documentation states that you are able to pass tasks instead of a task.

gulp-cli documentation states tasks instead of task

gulp-cli documentation states tasks instead of task

The plural of task matters here because normally you execute a single task from the command line. To integrate your custom task in the framework simply call gulp with two parameters. Like this.

gulp serve filewatch

Thats all you need to integrate a custom build task into the SharePoint Framework. First filewatch task will be executed followed by serve. You see this behavior clearly in the console.

SPFX - gulp serve and filewatch together

SPFX – gulp serve and filewatch together

Now you can develop in your custom folder and inside your web parts folder.

SPFX - List now file changes form custom folder integrate in the SharePoint Framework

SPFX – List now file changes form custom folder integrate in the SharePoint Framework

While SPFX monitors the web part and SharePoint released folders your custom task monitors your folders.
Let’s say something needs to be pre-compiled. The result of this process simply needs to be outputted or copied to the monitored folders SharePoint. This way you can use it directly inside your web part. Let’s say you like to use some Handlebar templates or other components. Precompiled them on your special folder and move it over to the web part.

The SPFX integrated solution

Yes, both tasks can now be executed at once just by calling gulp with two instead of one task specified. The problem I see. You need to remember to use both serve and filewatch. There is a better way you can do.

// if gulp task serve is registered
if (gulp.tasks.serve) {
    // push your custom task as a dependency to serve
    gulp.tasks.serve.dep.push('filewatch');
}

The ‘gulp’ variable inside your gulp file is a global javascript object that contains an object that stores all tasks. The code above simply checks if a gulp task named ‘serve’ exists. If the ‘serve’ task can be found in the overall lists of tasks add the custom task as a dependency. As mentioned all dependencies will be executed before the actual task.
To evaluate that all tasks are registered in the right order, simply excuse gulp --tree which outputs the task dependency tree.

SPFX - Task dependency tree

SPFX – Task dependency tree

Anytime you execute gulp all tasks will be evaluated and put in the right order. This is actually the reason why SPFX launch pretty slow. It has to dig deep down to the node_modules folder to find all available tasks and consolidate them. In addition this custom piece of code will be evaluated to and added as a dependency.

Final thoughts

From a Visual Studio developer perspective the new SharePoint Framework and especially the yeoman generator works like a perfectly equivalent of a Visual Studio Project template.
The approach Waldek described perfectly illustrates the ideas and architectural considerations of the overall SharePoint framework.
This way I showed here describes a more gulp and web development focused way. From a web developer perspective, it feels like the new SharePoint framework limits the possibilities and has solutions to problems that already have been solved. It’s clear that many things need to be developed, bundled and deployed in a special and SharePoint and Office 365 friendly way. Things that should be locked down.
In future and for the first major release I think it would be great to have various modules that can be combined together. For example the workbench, bundling and predefined tasks that needs to be treated special. Some other tools such as gulp, webpack, auto prefix should be accessible for modifications.
In case of gulp it is an easy take to inject custom task into SPFX. In other cases it might be complicated or even impossible.

Do I love the new SharePoint Framwork? Absolutly! Right now its a really nice developer preview. I just hope that many things will change in the final release.

Leave a Reply

Required fields are marked *.


This site uses Akismet to reduce spam. Learn how your comment data is processed.