I have been playing with Angular, Node and Gulp frameworks
for a while mostly with WebStorm IDE and recently started using TypeScript and
I love it. To learn and understand how these framework will integrate with
SharePoint solution, I created simple Web Part farm project which does nothing at
the moment, except few text boxes binding with Angular. WebStorm does not
support SharePoint Solution, but luckily the recent release of Visual Studio
2015 has built-in support for Node, Task Runner and Bower. You can also add Task
Runner support in Visual Studio 2013 by installing this
plugin.
This post is about my learning experience integrating all these technologies
together with SharePoint, Visual Studio and Gulp workflows. Here is the list of
tools/frameworks I used:
Node – JavaScript runtime engine
Angular – JavaScript framework
jQuery – JavaScript framework
Gulp – Automation and build workflow (install global only)
Typescript – Built-in support in VS.NET 2015. Other version requires WebEssentials
TSD – DefinitelyType definition for Typescript repository.
Bower – Front-end optimized framework/libraries package manager. VS.NET 2015 has built-in support for Bower.
NPM – Node package manager (comes with Node)
Git – required by NPM and Bower
You can open link for instructions to install on your environment.
Step 1 – Create SharePoint Project
Create empty SharePoint 2013 project and added SharePoint Project Item: EmpDir_Module. This is the folder where we will be generating compiled/bundled/minified typescript file and other JavaScript files which we will be deploying to SharePoint. You can create dummy placeholder files and include it in Elements.xml or once files are copied (by Step 6) you can use "Include in Project" and update Elements.xml.
Step 2 – Setup
I assume all tools are installed and ready to go. First we need to initialize *.json
files which contains references to files dependencies with versions installed, which
can be used by other developers to bring in all dependencies.
Open Command Prompt, navigate to root Project directory and run following
commands. I tried running this command from Visual Studio Package Manager
Console but it hanged. These commands will ask series of questions to
initialize *.json file.
$ npm init
- this command creates package.json file
$ bower init
- this command creates bower.json file
$ tsd init
- this command creates tsd.json and folder “typings”
Step 3 – Install Dependencies
Once we have all the *.json files, we can begin installing dependencies for our
project. We can run these command either from external command prompt of Visual
Studio Package Manager Console window.
$ npm install --save-dev gulp
- install gulp for the project and save as DEV dependency in package.json
$ npm install --save-dev gulp-concat gulp-print gulp-rename gulp-sequence gulp-typescript -uglify run-sequence
- install gulp plugins and save as DEV dependency in package.json
* Note : npm installs all components under “node_modules” folder in root.
$ bower install jquery angular --save
- install jquery and angular framework and save dependency in bower.json
* Note : bower installs all components under “bower_components” folder in root.
$ tsd install jquery angular --save
- install jquery and angular DefinitelyTyped definitions and save dependency in tsd.json
* Note : tsd installs all components under “typings” folder in root.
* Note :
--save switch will record dependency as minimum required and if latest
version available, will download the latest build. If you want to stick to any
version, you can use
--save-exact switch.
Step 4 – Write code
Before you start writing Typescript files, it’s better to decide the project folder
structure. There is awesome Angular style guide compiled by John Papa
here. As
we are not going to deploy *.ts (typescript files) to SharePoint, I prefer to
keep those files away in a separate folder. Here is how my project folder
structure looks like:
I have created folder: “App” which contains typescripts source and compiled JavaScript
files under “TScripts” and “compiled” folders. I have not included “bower_components”,
“node_modules” and its contents to the project, as we are not deploying these
files. We do need files from “bower_components” and will see how to deploy
those as Module. I added “typings” folder, as I sometimes open *.d.ts files to
see method signature or interface description. In
Visual Studio you can select any method on typescript file and hit F12 will take you to
method definition in *.d.ts file. Pretty handy.
I will not discuss about writing angular in Typescript, as that is a huge topic
in itself. Pluralsight has several trainings on this subject.
Typescript file naming convention is for a reason we will see when we go to
Gulp section. The naming convention will come handy during bundling and order
of files.
Step 5 – Write Gulp Workflow
This is where all magic happens. Create a new JavaScript file: “gulpfile.js” in
the root and copy/paste content below:
/// <binding BeforeBuild='default' />
/**
* Created by Javed on 8/15/2015.
*/
'use strict';
var gulp = require('gulp'),
gprint = require('gulp-print'),
runSequence = require('run-sequence'),
ts = require('gulp-typescript'),
bundle = require('gulp-concat'),
minify = require('gulp-uglify'),
rename = require('gulp-rename');
var paths = {
"TSSrc": 'App/TScripts/',
"JSCompiled": 'App/compiled/',
"JSDest": 'EmpDir_Module',
"BowerSrc": "bower_components/"
}
var config = {
"TypeScriptFiles": [
paths.TSSrc + '**.ts'
],
"BowerFiles": [
paths.BowerSrc + 'jquery/dist/jquery.min.js',
paths.BowerSrc + 'angular/angular.min.js'
],
"BundleSrc": [
paths.JSCompiled + '*.module.js',
paths.JSCompiled + '*.config.js',
paths.JSCompiled + '*.run.js',
paths.JSCompiled + '*.constant.js',
paths.JSCompiled + '*.directive.js',
paths.JSCompiled + '*.service.js',
paths.JSCompiled + '*.controller.js'
],
"BundleFileName": "ed.bundle.js",
"MinifyFileName": "ed.bundle.min.js"
}
gulp.task('compile', function () {
var tsc = gulp
.src(config.TypeScriptFiles)
.pipe(gprint())
.pipe(ts({ "noImplicitAny": true, "removeComments": true }));
return tsc.js.pipe(gulp.dest(paths.JSCompiled));
});
gulp.task('bower', function () {
return gulp
.src(config.BowerFiles)
.pipe(gprint())
.pipe(gulp.dest(paths.JSDest));
});
gulp.task('prod_build', function() {
return gulp
.src(config.BundleSrc)
.pipe(bundle(config.BundleFileName))
.pipe(gprint())
//.pipe(rename(config.MinifyFileName))
//.pipe(minify())
.pipe(gulp.dest(paths.JSDest));
});
gulp.task('default', function () {
runSequence('compile', 'bower', 'prod_build');
});
You can read more on Gulp
here. I will explain in brief
about gulpfile.js and each task.
As gulpfile.js is plain JavaScript file, you can define your own variable and functions. I have defined 2 variables “paths” and “config” objects with property for source,
destination or file name which we will be using in gulp tasks. I found this approach
to be better than hard coding paths in tasks definition itself.
Compile task:
- Grab all *.ts files under “App/TScripts” folder
- Compile to JavaScript
- Copy output to “App/compiled” folder
Bower task:
- Grab bower_component files like jquery, angular
- Copy to SharePoint module folder “EmpDir_Module”
Prod_Build task:
- Grab all compiled JavaScript from “App/compiled” folder in same order as defined in config.BundleSrc array
- Conact all files and save “ed.bundle.js”
- Rename file to “ed.bundle.min.js”
- Minify/uglify this file
- Copy to SharePoint module folder “EmpDir_Module”
Default task:
- Run all tasks in sequence.
* Note : When default task in defined executing “gulp” command with no
parameter runs default task. You can run individual task by executing “gulp
bower” from command line or VS Package Manager Console.
Step 6 – Attaching Gulp task with Visual Studio build event
The last step. Open Task Runner from View > Other Windows > Task Runner
Explorer. You should see Gulpfile.js node with all task names. Right click on
any task and select Bindings to attach to any build event. See the screenshot
below, where default task is attached to Before Build event.
* Note : While in development mode, you can write new task "dev_build" and instead of copying to Module folder, you can map Style Library or any other library (where you are deploying your .js files) and copy to this mapped location. No need to deploy WSP package :)
This concludes the long post. Hope this help someone.
Thanks,
-Javed