Build a Quick-Start with a jQuery Plugin, Theme and Git

This post is most appropriate for SuiteCommerce Advanced sites running Kilimanjaro and older. If you're interesting in theming and versioning an Aconcagua or newer, or SuiteCommerce, site, see Get Started with SuiteCommerce Themes.

This article is going to be somewhat of a bumper one as we cover a wide-range of topics from the point of view of a particular scenario: quick-starts.

The idea of a quick-start is not one that can be applied universally to all SuiteCommerce developers — in truth, it is best aimed at teams of developers who work in agencies: building sites for customers over and over again.

A quick-start is where you take the base SCA code, add in custom functionality, themes, improvements and fixes, and then package it up so that when the time comes to build a new site, a lot of work that your team normally does every time has already been done for them.

What we're going to do for this post is walk you through some of things you could do while creating your own quick-start and then packaging it up. So, for example:

  1. Take a fresh instance of SCA Elbrus and commit it to version control (Git) as the basis for the future
  2. Add in some simple custom functionality (a jQuery plugin called NProgress) and commit that
  3. Apply a theme (NightSkin, which comes with Elbrus) and commit that too

We'll be thinking about how we can package it so that it's easy to work with — after all, this process is about saving time and streamlining, so they'll be discussions around policy and suggestions on what you should be doing.

Preparing for Git

We've talked about getting started with Git before, so I won't dwell on it. You will need to download and install it before you can use it.

The point of using version control is that changes are tracked — you can see who did what and when, and at any point you can roll back to a particular point in its history.

Another great point — vital to our scenario — is that distribution is made super easy. After creating a local repository of your code and changes, you can push it up to a remote repository. Whenever a new project is about to begin, you can pull it down, create a new repository, and then push it up to that.

Furthermore, as no person is an island, you'll likely be working with colleagues on the project; by using version control, multiple developers can modify and contribute code at the same time, and the changes can be merged together — difficulty only really arises when two people modify the same files.

To get started, you'll need to go through the steps for setting up your dev environment, such as creating a fresh source version of SCA. Once everything is copied down and ready to go, you'll then need to make the initial commit.

// Ready the directory for use with Git
git init

// Check to see if the files are ready for staging
git status

// Stage all the files for committing
git add .

// Check to see if the files are staged
git status

// Commit the files
git commit -m "Initial commit"

// Check the commit history
git log

After that, we should then ready the remote repository and push it up.

The steps for this are listed in the getting started guide, but it essentially requires you to create an account with a Git service provider (eg Bitbucket or GitHub) and then create a new repo in that service. After that, these services usually let you select whether it'll be used from scratch or with existing code — select 'existing code' and you should see commands to run in the form of:

// One-time setup command to create the link between local and remote repos
git remote add origin ssh://git@[service]/[username]/[repo].git
// For example
git remote add origin ssh://git@bitbucket.org/sgoldberg/sca-elbrus-quickstart.git

// Push the code to the remote repo
git push -u origin master

With that pushed up, you can see the code stored online via the service you signed up for. It also means that we can now work on adding some functionality and actually adding value to the quick-start repo.

Implement a Custom Loading Bar Using the NProgress jQuery Plugin

One area that we can develop in our quick-start repo is custom functionality.

Something that our professional services team does — as well as a number of agencies — is use SCA as a source for 'base' functionality and then have a store of ready-made modules that are added onto every build.

What we're going to do as an example is add the NProgress loading bar to the site.

We've done this before, in a way, when we did a direct swap of the icon with one we made ourselves. The NProgress loading bar requires us to override the existing custom jQuery.ajaxSetup.js file with a new one. Much like the existing one, however, it adds event listeners to all AJAX calls and runs code in the plugin.

The effects that this plugin gives us are twofold:

  1. A smooth loading bar gently slides across the screen indicating the progress of the current AJAX operation
  2. The 'spinner' gif is replaced with a pure HTML/CSS version, which now appears in the top right corner of the page

The specifics of these two design items are customizable as well, so you can modify them in your quick-start and/or when the actual site build happens. For now, we're just going to integrate it into our code. Incidentally, if you want to include this functionality on your site then consider this a double helping of delicious tutorial delights.

Basic Setup

We do what we normally do: set up the folder and get things ready. One thing I'm going to do is suggest a slight alteration to the usual flow of things. What might be useful for something like a quick-start project, is to create a folder separate from your normal customizations directory and put the quick-start stuff in there: when it comes to building and maintaining a site that uses it, you or your team may find it useful to clearly separate what is custom to SCA (ie your quick-start modifications) and what is custom to the site itself.

In the Modules directory, create a folder called quickstart.

Next, we need to think about this functionality. There are essentially two components: the third-party library itself and the code that we have to write in order to integrate it into the SCA code. We should — and will — create two separate folders for each of these modules.

First, get the NProgress code: specifically, we want the JavaScript and CSS files. Place them in a folder called NProgress@0.2.0 and then rename nprogress.css to _nprogress.scss.

Within this folder, we will also require an ns.package.json file; create it and add the following to it:

{
  "gulp": {
    "javascript": [
      "NProgress.js"
    ],
    "sass": [
      "_nprogress.scss"
    ]
  }
}

This directory has only two files so we needn't bother sub-dividing with further ones.

Second, we need to prepare the module that will load and implement this functionality. Create NProgress.Loader with two sub-directories for JavaScript and Configuration — while this module will only have one file in each, it is conceivable that there could be further files added to this module.

Finally, create an ns.package.json file for this module:

{
  "gulp": {
    "javascript": [
      "JavaScript/*.js"
    ],
    "configuration": [
      "Configuration/*.json"
    ]
  },
  "overrides": {
    "suitecommerce/jQueryExtras@2.3.0/JavaScript/jQuery.ajaxSetup.js": "JavaScript/jQuery.ajaxSetup.js"
  }
}

With that, we can look at implementing the required jQuery.ajaxSetup.js code.

jQuery.ajaxSetup.js

What we learned from the last time we did something like this is that this file overrides the default code included with jQuery. It allows us to run arbitrary code whenever AJAX calls are made; in other words, this is the file we need if we're going to do stuff when things are loading.

Overriding a JavaScript file is unusual in SCA but it is necessary here as we need to replace parts of the existing file which cannot be extended or wrapped. There are still some important parts of the file that we will need in our new file, but we will have to copy and paste those.

If you take a look at the existing file, you'll see that the majority of it is set up to track the cursor and attach the spinner to it. However, the crucial part for us the bit that attaches listeners to the document.

The key part of the code we need add is:

$document = jQuery(document)
  .ajaxStart(NProgress.start)
  .ajaxStop(NProgress.done);

Those two functions are defined in the code for NProgress and are what do all the work. Anyway, to speed up things, let me give you the full code.

In JavaScript, create jQuery.ajaxSetup.js:

define('jQuery.ajaxSetup'
, [
    'NProgress'
  , 'SC.Configuration'
  , 'underscore'
  , 'jQuery'
  , 'Utils'
  ]
, function
  (
    NProgress
  , Configuration
  , _
  , jQuery
  )
{
  'use strict';

  var $document;

  NProgress.configure
  ({
    minimum: parseFloat(Configuration.layout.nprogress.minimumProgress),
    ease: Configuration.layout.nprogress.ease,
    speed: parseInt(Configuration.layout.nprogress.speed, 10),
    showSpinner: Configuration.layout.nprogress.showSpinner,
    trickleRate: parseFloat(Configuration.layout.nprogress.trickleRate),
    trickleSpeed: parseInt(Configuration.layout.nprogress.trickleSpeed, 10)
  });

  // Check we're running in the browser first
  if (typeof nsglobal === 'undefined')
  {
    $document = jQuery(document)
      .ajaxStart(NProgress.start)
      .ajaxStop(NProgress.done);
  }

  // fix to solve APM issue (timebrowser timing):
  if (_.result(SC.ENVIRONMENT, 'SENSORS_ENABLED'))
  {
    $document.ajaxStop(function() {
      if (typeof window.NLRUM !== 'undefined') {
        window.NLRUM.mark('done');
      }
    });
  }
  
  // If there's no charset, we set it to UTF-8
  jQuery.ajaxSetup
  ({
    beforeSend: function (jqXhr, options)
    {
      if (options.contentType.indexOf('charset') === -1)
      {
        jqXhr.setRequestHeader('Content-Type', options.contentType + '; charset=UTF-8');
      }
    }
  });
});

I don't think there's anything here that requires too much explanation:

  1. After the dependencies are declared, we set a bunch of settings pulled from the backend configuration object
  2. Next we add the event listeners, first checking to make sure that this code is running in the browser (and not the SEO page generator)
  3. Finally there are two blocks of code copied from the original file that we're also including

That's kinda it. From here, we just need to add in the configuration file for the values we just listed. To be honest, if you wanted to streamline this then you could just remove the entire configuration part (ie, do you want site administrators to be able to configure these options, or should it be left to the developers?) — however, I'm including it. And because I am including it, we need to add a configuration file to the module.

NProgress.json

In Configuration, create NProgress.json with the following settings:

{
  "type": "object"
, "subtab": {
    "id": "nprogress"
  , "title": "NProgress Bar"
  , "group": "layout"
  , "description": "Progress Bar"
  }
, "properties": {
    "layout.nprogress.showSpinner": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "boolean"
    , "title": "Also Show Spinner"
    , "default": true
    }
  , "layout.nprogress.trickleRate": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "number"
    , "title": "Trickle Rate"
    , "default": 0.08
    }
  , "layout.nprogress.trickleSpeed": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "integer"
    , "title": "Trickle Speed (ms)"
    , "default": 140
    }
  , "layout.nprogress.speed": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "integer"
    , "title": "Speed (ms)"
    , "default": 200
    }
  , "layout.nprogress.ease": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "string"
    , "title": "Easing"
    , "default": "ease-in-out"
    }
  , "layout.nprogress.minimumProgress": {
      "group": "layout"
    , "subtab": "nprogress"
    , "type": "number"
    , "title": "Progress Start"
    , "default": 0.1
    }
  }
}

This creates a new sub-tab within the Layout tab for the user to enter their options. We've assigned some defaults — note they are a little different to the defaults in NProgress.js, but as the developer you can choose the ones you think best. Take a look at the settings object in NProgress.js and see which ones you might want to expose (I probably wouldn't allow a site admin to change the template, for example).

Update distro.json and Deploy

Finally, we're ready to push it up and see if it works. You'll need to add the two new modules to the modules array and then NProgress to the CSS of each application. We don't actually don't need add either module to the dependencies of any of the applications.

Save everything and deploy. When you test your site, you should see the fancy new loading bar and animation and that everything is working as intended.

Commit and Add to Repo

Now we're going to add this work to our quick-start repo.

If you run git status you should see a list of modified files (mostly distribution files) as well as our new module which is 'untracked'. If a directory or file is untracked, that means that changes to those are not being recorded by Git; thus if you were to make a commit without adding those files first, they would be ignored and not added to the repository. Let's go through the steps again:

// Check the status
git status

// Stage the new files
git add .

// Check all files are green (staged)
git status

// Commit them
git commit -m "Added NProgress loading bar"

// Push them up to the remote repo
git push origin master

You can run git log or head over to your remote repository to check the changes have gone through. With that, let's focus on the next thing: our theme.

Theming

Broadly speaking, there are three areas of the application that you can change to implement a new 'look and feel' of a website: Sass, templates and JavaScript. The most basic theme changes involve changing only the Sass: this will keep the arrangement of elements and the data provided to them exactly the same while making changes only to things like colors, fonts and backgrounds.

For simplicity's sake, we're going to look at this option: we're going to use a theme included in Elbrus, which only makes superficial changes, as the basis for this.

If you take a look at the themes directory in the Modules directory, you'll see that we have a Sass folder that contains an override for _colors.scss and another with some extras in it.

A theme's changes should be contained in one module where possible — this ensures quick and easy installation/removal when required. It also highlights how you should minimize the amount of overrides and fundamental changes you make. There will certainly be problems if two modules override the same file.

If you look at the README.md file, we've included instructions on how to enable this functionality:

  1. In distro.json, ensure the module is included in the modules array (it is by default)
  2. In distro.json, add it to CSS of the applications we want to include it in (might as well do all of them)
    {
        "module": "NightSkin",
        "include": ["night_skin_extras"]
    }
  3. Add an override to the ns.package.json file for the module

Make the changes and then push up the changes to your site and take in how marvellous your new site looks!

Beautiful.

Commit Theme to Git

Now we can also apply the new changes to our remote repo. You can do git status to see what we're dealing with. As we're not adding a new files we can shortcut the commit process and simply do:

git commit -a -m "Applied Nightskin theme"

The -a flag automatically stages all files that have been modified or deleted meaning we don't need to use a separate command for it. The thing to note, however, is that it's not useful when you've added new files — they won't get picked up.

Don't forget to git push origin master!

'Start a New Site Build'

I've put that in quotes because we're obviously not going to start building a new site, but I do want to run through the process to give you an idea of what it's like working with Git to get new projects up and running.

Now that you've got your fancy new quick-start in the cloud, you can pull it down whenever you want to start using it. If necessary, you can grant permissions to additional users so that they can also pull it down.

Before you begin, however, you'll need to think about how you want to proceed in terms of how your sites' code bases relate to quick-start repo. For example, you could keep one master build of your quick-start and track all sites you build from it as forks. Forks are remote copies of a repository that can feed back into the master copy but not necessarily; if you're going to use them for site builds it's very unlikely they would. Forks are, however, extremely easy and transparent ways of creating clones of repos.

Head over to your repo in whatever service you chose (I'm using Bitbucket, so all of what I'm about to describe is in their interface). You should see your super fresh commits.

In the side panel, click the + and then Fork this repository. Set the name for the repo — a good naming convention might be "SCA-[code version]-[site name]", such as "SCA-Elbrus-IntergalacticShoes".

Once you've done that you can now pull down the code from the net. Go to the root directory on your computer where you would like the code to live and then do:

// This
git clone [repo location]

// For example
git clone git@bitbucket.org:sgoldberg/sca-elbrus-intergalacticshoes.git

Then if you move into that directory, you'll see that it's a complete copy of the files you were just working on. Keep in that in mind. It is a complete copy. So, for example, it means that commands like gulp work straight off the bat and that if you do gulp deploy it will deploy using the saved settings.

You'll also note that the repos we created include the local and deployed distributions of the code. These are not necessary for our projects: they're only required to run the site, not code it, and new ones are generated each time we run the relevant Gulp commands. So why do we keep them? Well, we don't have to.

Make Git Ignore Certain Files and Directories

One of the things we can do is create a .gitignore file that tells Git to intentionally ignore specific untracked files. As their documentation says, once a file has been tracked by Git, it is not affected by changes to the .gitignore file. So, erm, sorry about that. Maybe we should have covered this earlier?

Well, I wanted to bring this up now to show why we need this functionality (rather than simply taking my word for it). Besides, you haven't actually just created a quick-start repo using that terrible theme, have you? No. Good. It was just a dry run.

To start, let's make a copy of the quick-start. In your terminal, change to the new directory and then this command (making absolutely sure that you're in the right directory and that you type it in correctly):

// Remove all git files
rm -rf .git

This will remove all record of this being a git repo, meaning that you can re-initialize it if you like.

In your text editor, create a new file in the root of this SCA directory (the same level as distro.json) and name it .gitignore. Put the following content in:

# Ignore the distros
DeployDistribution/
LocalDistribution/
.ns-deploy

Here we've specified three things not to track: the two distribution directories and the Gulp configuration file. If you run git init && git status you'll see that they don't show up anymore. Now, you can commit and push up to a new remote repo!

As an aside, if you have already started your quick-start repo and I just messed it up, what you can do is the following:

  1. Run gulp clean to remove the distribution directories
  2. Delete .ns-deploy
  3. Commit and push up the deletions to the remote repo
  4. Remove tracking for those files: git rm -r --cached LocalDistribution/ && git rm -r --cached DeployDistribution/ && git rm --cached .nsdeploy
  5. Create the .gitignore file as described above
  6. Run gulp

When it's finished and the local distribution folder has been created, you can run git status again and see that it hasn't been included in the list of files being watched. Success!

Final Thoughts

The idea of creating and using quick-starts is something that is widespread throughout the industry. It means that as a developer or agency, you can ramp up new builds much more quickly. It turns repetitive tasks into ones that you only have to do once.

At NetSuite, it's something we do and we know that a number of our agency partners do too (eg Tavano's Themes Express, Explore's FastTrack and Sixred's QuickStart). While I can't tell you what they include beyond what information they have on their websites, it should make you think about your own situation and what you're doing over and over again.

We talked about the types of things you can put into your quick-start: we opted for an item of functionality and a very (very) attractive theme. As a team, you can start to build up these items yourself and think about interoperability. For example, are there items of functionality that you always want to include? What about those that address specific customer issues? Do you have ones that are mutually exclusive.

We looked at Git — distributed version control — and how we can use to track our changes and easily distribute repositories to colleagues so that they can start new projects. When they do this, they create a fork of the repository indicating that they've split from the mainline of the code. Any changes they make could theoretically be merged back in, but if it's for a site build then it'll stay separate. When this code, they can also create their own online repo, and so on.

Lastly, we looked at using a .gitignore file to prevent certain files and directories from being included when committing files. This is useful for a couple of things: the distribution files, and the deploy file containing build/site-specific details; or, in other words, we use it temporal and potentially sensitive files from being included. This is good as it cuts down the size of the repository and cuts down on the amount of noise in commits.

What's your experience with quick-starts? Does your organization use one? What does it include?