Skip navigation
All People > ddraper > Dave Draper's Blog > 2016 > December > 28

My First NPM Package

Posted by ddraper Dec 28, 2016
This is a personal blog post that is primarily intended for tracking my own learning rather than provided to the Alfresco Community for educational purposes. However if you find it useful, informative or have any comments on it then please comment below.

 

At the end of my last blog post I was looking at ways in which to write better components by abstracting as much non-browser specific code to utility modules. The idea of this was to improve testing so to minimize the amount of code that needed to be tested against multiple browsers. The side-effect of this approach means producing common capabilities that can be easily re-used against multiple web application frameworks such as Vue.js, React, Angular, etc.

 

As I developed my application I found myself pulling in more and more code and configuration that was already available in one of the many Vue.js CLI template projects. Most UI frameworks now come with a CLI that makes it easy to rapidly build out an application. I want to test out other frameworks so I thought it would make sense to abstract the core authentication and routing code into a separate project so that it could be imported into multiple different projects created using a CLI.

 

I followed some of the approach in this blog post and then mostly following these instructions on setting it up as an NPM package. Some useful information on testing the package prior to publication in described here. It was also necessary to follow this information in order to create a package with multiple exports.

 

The end result means that it is now possible to to install this new package with the following command:

npm install alfresco-auth-router --save-dev

 

Or if you prefer Yarn:

yarn add --dev alfresco-auth-router

 

Then it is just a simple case of setting up the Passport.js and Express routing using the following code in the server.js file:

passport.use(alfrescoAuthRouter.passportStrategy());
passport.serializeUser(alfrescoAuthRouter.serializeUser);
passport.deserializeUser(alfrescoAuthRouter.deserializeUser);
server.use('/auth', alfrescoAuthRouter.authRoutes(passport));
server.use('/proxy', alfrescoAuthRouter.apiRoutes());

 

This should make it much simpler to test out lots of different UI frameworks on Node.js against REST APIs provided by an Alfresco Repository. You can view this in my project by checking out this tag.

This is a personal blog post that is primarily intended for tracking my own learning rather than provided to the Alfresco Community for educational purposes. However if you find it useful, informative or have any comments on it then please comment below.

 

At the end of my last blog post I had got some initial unit tests working using Karma for my Vue.js client running on a Node.js stack. One of the great things about Aikau is the automated testing that we setup in an attempt to prevent us introducing regressions into our code. The main problem that we now have is that a full regression test on 2 browsers takes around an hour to complete. Whilst this is a reasonably manageable amount, it is not ideal for continuous integration testing where ideally you want to capture bugs as soon as they're introduced.

 

The other thing that I wish I had done differently was to create a greater separation between the "pure" JavaScript code and the code that was tied to the working with the browser DOM. In theory it should only be necessary to test web application code on different browsers where the code may actually behave differently - this would typically be when it was working with the DOM to build the interface that is used. It is of course true that different browsers have different JavaScript engines, but typically there shouldn't be a huge amount of difference - especially if you're coding to the lowest common denominator. 

 

There is a good example of this in the breadcrumb component that I am building. In the Vue.js component we are "watching" (i.e. responding to changes on) the relative path attribute. When a change to the value occurs the component builds a new breadcrumb trail from this updated path:

watch: {
   relativePath: function() {
      let lastPathElement = '/';
      this.breadcrumbs = [{
         label: 'Home',
         relativePath: lastPathElement
      }];
      this.relativePath
         .split('/')
         .filter(function(name) {
            return name.trim() !== '';
         })
         .forEach(function(pathElement) {
            let relativePath = lastPathElement + pathElement + '/'
            this.breadcrumbs.push({
               label: pathElement,
               relativePath: relativePath
            });
            lastPathElement = relativePath;
         }, this);
   }
},

 

This function is highly unlikely to behave any differently when run in Chrome, Firefox or Internet Explorer (especially since it's getting transpiled through Babel to ES5). As it is currently written it would have to be tested with multiple permutations on multiple browsers and because it is embedded within a Vue.js component it would also be necessary to drive the tests through the browser and handle all the associated waiting around for the DOM to be updated, etc. 

 

A much better solution would be to abstract this function to a separate module that could be much more easily tested. If it was placed in a module like this:

export default class Breadcrumb {
   static createBreadcrumbs(input) {
      let lastPathElement = '/';
      let breadcrumbs = [{
         label: 'Home',
         relativePath: lastPathElement
      }];
      input.relativePath
         .split('/')
         .filter(function(name) {
            return name.trim() !== '';
         })
         .forEach(function(pathElement) {
            let currRelativePath = lastPathElement + pathElement + '/';
            breadcrumbs.push({
               label: pathElement,
               relativePath: currRelativePath
            });
            lastPathElement = currRelativePath;
         });
      return {
         lastPathElement: lastPathElement,
         breadcrumbs: breadcrumbs
      };
   }
}

 

...it could then be used in the component like this:

watch: {
   relativePath: function() {
      let breadcrumbData = BreadcrumbUtil.createBreadcrumbs({
         relativePath: this.relativePath
      });
      this.breadcrumbs = breadcrumbData.breadcrumbs;
   }
},

 

And have a much simpler tests written for it like this:

describe('Breadcrumb Util', () => {
   it('should generate home breadcrumb for root', () => {
      let breadcrumbs = BreadcrumbUtil.createBreadcrumbs({
         relativePath: '/'
      });
      expect(breadcrumbs.breadcrumbs).to.have.lengthOf(1);
  });
});

 

As well as the tests being faster and more reliable, it has one other major advantage.... this utility function could be easily used by applications built using other UI frameworks such as React or Angular. 

 

I think that this would be a good approach to developing and testing web application components - the tests could be running constantly and incredibly quickly in the background to give you immediate feedback when you make any changes that break a test and the code would be much more easily portable and re-usable between frameworks.