Front-end development with Hubspot and Grunt Part 2

So, the last post talked about the hubspot set up. This part talks about how we used grunt to automate certain aspects of the build process.

Grunt

We’ve set up a number of tasks to help automate the build process, depending on what environment is being targeted.

  1. grunt-contrib-sass – fairly self-explanatory, this compiles the sass. We added a little flag into the mix so it conditionally compresses the output.
  2. grunt-autoprefixer – standard stuff to add vendor prefixes
  3. grunt-contrib-uglify – Concatenate and compress js again with the flag to conditionally compress (see gotchas)
  4. grunt-contrib-copy – copies images/templates between environments
  5. grunt-cdnify – This is quite a critical one, since all assets need to be referenced from the the hubspot cdn url that is provided when the uploader is uploading files. This not only includes css/js/img refs but also background image urls in css.
  6. grunt-string-replace – This one is used to modify the hubspot meta data so that assets can be uploaded into the right area.

I’ll just go through a couple of the more critical tasks here – cdnify and string-replace, but first, there’s a bit of config that needs to be done.

Hubspot grunt config

This config is used throughout all the tasks as a way to specify the cdn reference to hubspot, as well as the paths to the various environments. Here it is…

var hubSpotConfig = {
 env: 'flat',  // 1
 copySrc: 'flat',  // 2
 cdn: {  // 3
   original: '//temp.hubspot.net/hubfs/001',
   updated: '//cdn2.hubspot.net/hubfs/467997'
 },
 flat: {  // 4
   templates: 'flat/templates',
   files: 'flat/files'
 },
 dev: {
   templates: 'dev/templates',
   files: 'dev/files/dev'
 },
 prod: {
   templates: 'prod/templates',
   files: 'prod/files/prod'
 } 
};
  1. env  – The current environment, defaults to ‘flat.’ So when running the grunt task, an environment flag can be set to conditionally target either the ‘dev’ or ‘prod’ environments.
  2. copySrc – If the copy task is to be run, where should the files be copied from. Defaults to ‘flat’
  3. cdn – The original and updated urls that are used by the cdnify task. Before initial upload, the actual cdn reference is unknown. Also, by specifying an ‘original’ and an ‘updated’ reference, this means that if the cdn ref changes, its not a problem
  4. flat, dev, prod – The relative paths to the ‘files’ and ‘templates’ folders for each environment

CDNify

This is a pretty useful one, since without it, you’d need to go through every single url ref and change it to the hubspot cdn reference (which you’re provided with when the files are uploaded). With this plugin, you can specify a rewriter function that will get every url in the list of files you select, passed into it. Then, you can conditionally update it or just return the original.

So, our rewrite function looks like this…

rewriter: function(url) {

// 1
var matchUrl1 = new RegExp("(^assets|^\.\.\/|^"+ 
 escapeRegExp(hubSpotConfig.cdn.original) + "\/("+ grunt.option('copySrc') + '|' + grunt.option('env') +")\/)"),

// 2
matchUrl2 = new RegExp("^{{.*\/"+ grunt.option('copySrc')),

// 3
cdnUrl = hubSpotConfig.cdn.updated + '/' + grunt.option('env') + '/',

// 4
regexp = new RegExp("(^assets\/|\.\.\/files\/|(\.\.\/){4}components\/|" + escapeRegExp(hubSpotConfig.cdn.original) + '(\/' + grunt.option('copySrc') + '\/' +
 "|\/"+ grunt.option('env') + "\/))?"),

// 5
if(url.search(matchUrl1) === -1 && url.search(matchUrl2) === -1) {
  return url;
}

// 6
if(url.search(matchUrl1) !== -1) {
  newUrl = url.replace(regexp, cdnUrl);
} else {
  newUrl = url.replace(regexp, '/' + grunt.option('env') + '/');
}
 
return newUrl;

}

Whoa! Some stuff going on there. Below is an explanation (ish)

  1. Regular expressions are used to match the urls that come into the function. If the urls don’t match, the original url is returned (3). matchUrl1 matches anything that starts with ‘assets’ or ‘../’ or a reference to an old hubspot cdn (see hubspot grunt config above) in either the dev or prod environments. The call to ‘escapeRegExp’ is a little function that escapes any characters reserved in regular expressions like dots, forward slashes etc.
  2.  matchUrl2 matches anything that starts with hubspots markup language delimiters – {{, followed by anything, followed by where assets are being copied from (generally either flat or dev). This is to catch urls that have already been converted to hubspot references but where the reference may have changed
  3. This specifies what the cdn reference should be based on the cdn reference provided by hubspot followed by the environment being targeted
  4. The actual regular expression for replacement
  5. If the url doesn’t match anything, return the original url
  6. If the url matches the first regexp, replace the relevant bits of the url using the main regular expression
  7. If the url matches the second regexp, replace reference to the environment only (this is mainly used when transferring content between dev and prod environments

The joy of regular expressions, eh?

String replace

This is another automation tool that does what it says on the tin – replaces strings based on matched patterns. We used this one to replace references to the ‘dev’ environment when transferring content between ‘dev’ and ‘prod.’ I realise now that we could probably have done the ‘cdnifying’ using this plugin, but by the time I realised we needed it, I’d already got cdnify working.

options: {
 replacements: [{ // 1
 pattern: /"path"\:.*(dev)/,
 replacement: '"path": "<%= hubSpotConfig.env %>'
},
{
 pattern: /\s*("id.*,)\s*/g,  // 2
 replacement: '\n'
},
{
 pattern: /({%.*\/)(dev)/, // 3
 replacement: '$1prod'
},
{
 pattern: /\/dev\//g,  // 4
 replacement: '\/prod\/' 
}
],
 saveUnchanged: false  // 5
},
files: [{
 expand: true,
 cwd: '<%= hubSpotConfig.env %>',
 src: '**/*',
 dest: '<%= hubSpotConfig.env %>'
}]
  1.  You need to specify an array of replacement patterns. The first one matches the ‘path’ line in any file with hubspot meta data in it, replacing it with the environment being targeted (see below ‘running tasks’)
  2. This pattern matches the ‘id’ line of the metadata, stripping it out, since files that live in the dev and prod environments of hubspot will have different id’s.
  3. Target any string that starts with the hubspot delimiters replacing reference to the ‘dev’ environment
  4. Find any other reference to the ‘dev’ environment and replace it with ‘prod.’
  5. saveUnchanged is a flag, if true, will save out every file it analyses regardless of whether any matches were found or not.

Running tasks

We only set up one task for ease of use. This meant that designers who maybe weren’t too comfortable with using the command line could easily run the tasks simply by typing ‘grunt.’

grunt.registerTask('default', 'conditional compilation', function() {

// 1
 hubSpotConfig.env = grunt.option('env') || 'flat';
 grunt.task.run([
 'uglify:common',
 'sass:common',
 'autoprefixer:common'
 ]);

// 2
 if(hubSpotConfig.env !== 'flat') {
   grunt.option('copySrc', hubSpotConfig.env == 'dev' ? 'flat' : 'dev');
   hubSpotConfig.copySrc = grunt.option('copySrc');

// 3 
   if(grunt.option('copy')){
     if(grunt.option('copy') === 'all') {
       grunt.task.run([
         'copy:common'
       ]);
     } else {
       grunt.task.run([
         'copy:images'
       ]);
     }
   } 

// 4
 if(grunt.option('cdnify')){
    grunt.task.run([
      'cdnify:common'
    ]);
 }

// 5
 if(hubSpotConfig.env === 'prod') {
    grunt.task.run([
      'string-replace:common'
    ]);
 }

 
}

});
  1. Check whether an ‘env’ flag was set. If not, set it to ‘flat’ meaning that by default, the flat environment will be targeted. commands: grunt or grunt –env=dev or grunt –env=prod
  2. If the environment is not flat, determine where files should be copied from.
  3. If the copy flag was set, either copy everything or just images. commands: grunt –copy=true or grunt –copy=all
  4. If the cdnify flag was set, run the associated task. commands: grunt –cdnify=true
  5. If the environment is prod, run the string-replace task

If the cos_uploader tool is running while the grunt tasks are being executed, resultant file will automatically be uploaded.

That’s about it

With this setup, we have a solution that allows designers to work on their flat sites, allows developers to integrate into hubspot without too much copying and pasting and goes some way to make the site easier to integrate. I know that there are bits that need work here but for a first attempt at devising a workable workflow process, I think it ticks a few boxes. I think the next thing to look at is the local hubspot server that can be downloaded from hubspot. In theory this would mean we could develop and test hubspot integration without having to upload to a real site.

If you’ve got any suggestions, or lessons you’ve learnt about working with hubspot, please add a comment.

 

 

One thought on “Front-end development with Hubspot and Grunt Part 2

Leave a Reply