Last Updated: October 07, 2020
·
14.77K
· Ionut-Cristian Florescu

Managing assets in ASPAX vs Grunt

A couple of weeks ago I published a protip about ASPAX - a simple Node.js asset packager that enables you to manage your client-side assets based on a single human-readable file in YML format.

Some people were asking for a comparison between ASPAX and Grunt.js, so...

Managing assets in ASPAX vs Grunt

Here's a sample aspax.yml file:

# Main application file
js/app.js|fp|min:
  - lib/bootstrap/js/bootstrap.js
  - lib/moment.js
  - lib/jade/runtime.js
  - scripts/namespaces.coffee|bare
  - templates/now.jade
  - scripts/index.ls|bare

# Main CSS
css/app.css|fp|min:
  - lib/bootstrap/css/bootstrap.css
  - lib/bootstrap/css/bootstrap-theme.css
  - styles/index.styl|nib

# Images
favicon.png:            images/favicon.png

# Font icons
fonts/bs-glyphs.eot|fp: lib/bootstrap/fonts/glyphicons-halflings-regular.eot
fonts/bs-glyphs.svg|fp: lib/bootstrap/fonts/glyphicons-halflings-regular.svg
fonts/bs-glyphs.ttf|fp: lib/bootstrap/fonts/glyphicons-halflings-regular.ttf
fonts/bs-glyphs.woff:   lib/bootstrap/fonts/glyphicons-halflings-regular.woff

...and here's a Gruntfile.js you'd have to write in order to achieve the same results:

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({

    // CoffeeScript files compilation
    coffee: {
      compile: {
        options: { bare: true },
        files: {
          '../tmp/namespaces.js': '../client/scripts/namespaces.coffee'
        }
      }
    },

    // LiveScript files compilation
    livescript: {
      compile: {
        options: { bare: true },
        files: {
          '../tmp/index.js': '../client/scripts/index.ls'
        }
      }
    },

    // Jade client-side templates compilation
    jade: {
      compile: {
        options: {
          compileDebug: false,
          client: true
        },
        files: {
          '../tmp/now.js': '../client/templates/now.jade'
        }
      }
    },

    // Stylus files compilation
    stylus: {
      compile: {
        options: {
          compress: false
        },
        files: {
          '../tmp/index.css': '../client/styles/index.styl'
        }
      }
    },

    // Concatenate CSS and JS files
    concat: {
      js: {
        files: {
          'public/js/app.js': [
            '../client/lib/bootstrap/js/bootstrap.js',
            '../client/lib/moment.js',
            '../client/lib/jade/runtime.js',
            '../tmp/namespaces.js',
            '../tmp/now.js',
            '../tmp/index.js'
          ]
        }
      },
      css: {
        files: {
          '../tmp/app.css': [
            '../client/lib/bootstrap/css/bootstrap.css',
            '../client/lib/bootstrap/css/bootstrap-theme.css',
            '../tmp/index.css'
          ]
        }
      }
    },

    // Copy miscellaneous files and process asset URLs in CSS files
    copy: {
      css: {
        src: '../tmp/app.css',
        dest: 'public/css/app.css',
        options: {
          process: function(content, srcPath) {
            // Should replace asset URLs in CSS file
            console.log('Should process asset URLs in', srcPath);
            return content;
          }
        }
      },
      misc: {
        files: [
          { src: '../client/images/favicon.png',                                    dest: 'public/favicon.png'          },
          { src: '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.eot',  dest: 'public/fonts/bs-glyphs.eot'  },
          { src: '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.svg',  dest: 'public/fonts/bs-glyphs.svg'  },
          { src: '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf',  dest: 'public/fonts/bs-glyphs.ttf'  },
          { src: '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.woff', dest: 'public/fonts/bs-glyphs.woff' }
        ]
      }
    },

    // Watch and trigger compilation, concatenation, ...
    watch: {
      js: {
        files: [
          '../client/lib/bootstrap/js/bootstrap.js',
          '../client/lib/moment.js',
          '../client/lib/jade/runtime.js',
          '../client/scripts/namespaces.coffee',
          '../client/templates/now.jade',
          '../client/scripts/index.ls'
        ],
        tasks: ['coffee', 'jade', 'livescript', 'concat:js']
      },
      css: {
        files: [
            '../client/lib/bootstrap/css/bootstrap.css',
            '../client/lib/bootstrap/css/bootstrap-theme.css',
            '../client/styles/**/*'
        ],
        tasks: ['stylus', 'concat:css']
      },
      misc: {
        files: [
          '../client/images/favicon.png',
          '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.eot',
          '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.svg',
          '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf',
          '../client/lib/bootstrap/fonts/glyphicons-halflings-regular.woff',
        ],
        tasks: ['copy']
      }
    }

  });

  // Load plugins
  grunt.loadNpmTasks('grunt-contrib-coffee');
  grunt.loadNpmTasks('grunt-livescript');
  grunt.loadNpmTasks('grunt-contrib-jade');
  grunt.loadNpmTasks('grunt-contrib-stylus');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-csso');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Tasks
  grunt.registerTask('default', [
    'coffee', 'jade', 'livescript', 'concat:js',
    'stylus', 'concat:css',
    'copy',
    'watch'
  ]);

};

As you can see, aspax.yml is just 23 lines long, including comments - and quite easy to comprehend, I might say - while Gruntfile.js got to more than 150 lines, without even addressing fingerprinting and URLs adjustment in CSS files (I got bored and finally gave up, I admit :-).

Don't get me wrong, I am a fan of Grunt, I think it's an excellent general-purpose tool, I like it and use it when necessary, but I'm also a fan of using the right tool for the right job, and obviously writing an asset management Gruntfile can easily become a burden.

So, why not giving ASPAX a try?... It's still under development, of course, so we may still change/refactor a few things during the next couple of weeks, but the main functionality is there.

4 Responses
Add your response

My opinion is:

  1. Grunt configuration syntax is suitable when you need to perform one action for all files. For example: compile all coffee files (src/*.coffee) or lint all js files
  2. Aspax configuration syntax is suitable when you need to perform multiple different actions for single file or group of concatenated files. For example: concatinate js files, minify result, copy result for aplication and just minify and copy for libraries
over 1 year ago ·

@mahnunchik - my thoughts exactly, but I usually have to perform multiple/mixed actions on my assets.

I was trying to switch to Grunt when I first came across it a few months ago, but didn't really like the idea of having to write so much code. Here's a "real-life" sample aspax.yml:

js/index.js:
  - lib/swiper/idangerous.swiper-2.3.js
  - lib/moment/moment.js
  - lib/moment/lang/ro.js
  - lib/jquery.unveil.js
  - scripts/public/jquery.lsh.coffee   | bare
  - scripts/public/common.coffee       | bare
  - scripts/public/main-gallery.coffee | bare
  - scripts/public/product.coffee      | bare
  - scripts/public/search-form.coffee  | bare
  - scripts/public/contact-form.coffee | bare

js/admin.js:
  - lib/underscore.string.js
  - lib/jquery.ui.core.js
  - lib/jquery.ui.widget.js
  - lib/jquery.ui.mouse.js
  - lib/jquery.ui.sortable.js
  - lib/jquery.iframe-transport.js
  - lib/jquery.fileupload.js
  - lib/jquery.autosize.js
  - scripts/admin/jquery.lsh.admin.coffee | bare
  - scripts/admin/user-list.coffee        | bare
  - scripts/admin/dealer-list.coffee      | bare
  - scripts/admin/make-list.coffee        | bare
  - scripts/admin/meta-model-list.coffee  | bare
  - scripts/admin/model-list.coffee       | bare
  - scripts/admin/attribute-list.coffee   | bare
  - scripts/admin/product-edit.coffee     | bare
  - lib/jade-runtime.js
  - templates/namespace.coffee            | bare
  - templates/user-edit.jade
  - templates/dealer-edit.jade
  - templates/make-edit.jade
  - templates/meta-model-edit.jade
  - templates/model-edit.jade
  - templates/attribute-edit.jade

css/index.css:
  - lib/icomoon/style.css
  - lib/swiper/idangerous.swiper.css
  - styles/index.styl | nib

css/admin.css:
  - styles/admin.styl | nib

css/product-print.css:
  - styles/product-print.styl | nib

images/logo.png                     | fp : images/logo.png
images/logo@2x.png                  | fp : images/logo@2x.png
images/carbon-texture.png           | fp : images/carbon-texture.png
images/carbon-texture@2x.png        | fp : images/carbon-texture@2x.png
images/carbon-texture-light.png     | fp : images/carbon-texture-light.png
images/carbon-texture-light@2x.png  | fp : images/carbon-texture-light@2x.png
images/barcode.png                  | fp : images/barcode.png
images/barcode@2x.png               | fp : images/barcode@2x.png
images/breadcrumbs-separator.png    | fp : images/breadcrumbs-separator.png
images/breadcrumbs-separator@2x.png | fp : images/breadcrumbs-separator@2x.png
images/spinner-140x105.gif          | fp : images/spinner-140x105.gif

favicon.png                              : icons/favicon.png
apple-touch-icon-57x57-precomposed.png   : icons/apple-touch-icon-57x57-precomposed.png
apple-touch-icon-72x72-precomposed.png   : icons/apple-touch-icon-72x72-precomposed.png
apple-touch-icon-114x114-precomposed.png : icons/apple-touch-icon-114x114-precomposed.png
apple-touch-icon-144x144-precomposed.png : icons/apple-touch-icon-144x144-precomposed.png

fonts/im.eot  | fp : lib/icomoon/fonts/icomoon.eot
fonts/im.svg  | fp : lib/icomoon/fonts/icomoon.svg
fonts/im.ttf  | fp : lib/icomoon/fonts/icomoon.ttf
fonts/im.woff | fp : lib/icomoon/fontsfonts/icomoon.woff

Can you imagine the trouble of writing a Grunt scenario for that thing?...

over 1 year ago ·

@icflorescu in my project I have divided tasks between grunt and "asset tool" for example like this:

config.yml

js/index.js:
  - lib/swiper/idangerous.swiper-2.3.js
  - lib/moment/moment.js
  - lib/moment/lang/ro.js
  - lib/jquery.unveil.js
  - scripts/public/index.js

js/admin.js:
  - lib/underscore.string.js
  - lib/jquery.ui.core.js
  - lib/jquery.ui.widget.js
  - lib/jquery.ui.mouse.js
  - lib/jquery.ui.sortable.js
  - lib/jquery.iframe-transport.js
  - lib/jquery.fileupload.js
  - lib/jquery.autosize.js
  - scripts/admin/admin.js
  - lib/jade-runtime.js
  - templates/templates.js

css/index.css:
  - lib/icomoon/style.css
  - lib/swiper/idangerous.swiper.css
  - styles/index.css

css/admin.css:
  - styles/admin.css

css/product-print.css:
  - styles/product-print.css

images/logo.png                     | fp : images/logo.png
images/logo@2x.png                  | fp : images/logo@2x.png
images/carbon-texture.png           | fp : images/carbon-texture.png
images/carbon-texture@2x.png        | fp : images/carbon-texture@2x.png
images/carbon-texture-light.png     | fp : images/carbon-texture-light.png
images/carbon-texture-light@2x.png  | fp : images/carbon-texture-light@2x.png
images/barcode.png                  | fp : images/barcode.png
images/barcode@2x.png               | fp : images/barcode@2x.png
images/breadcrumbs-separator.png    | fp : images/breadcrumbs-separator.png
images/breadcrumbs-separator@2x.png | fp : images/breadcrumbs-separator@2x.png
images/spinner-140x105.gif          | fp : images/spinner-140x105.gif

favicon.png                              : icons/favicon.png
apple-touch-icon-57x57-precomposed.png   : icons/apple-touch-icon-57x57-precomposed.png
apple-touch-icon-72x72-precomposed.png   : icons/apple-touch-icon-72x72-precomposed.png
apple-touch-icon-114x114-precomposed.png : icons/apple-touch-icon-114x114-precomposed.png
apple-touch-icon-144x144-precomposed.png : icons/apple-touch-icon-144x144-precomposed.png

fonts/im.eot  | fp : lib/icomoon/fonts/icomoon.eot
fonts/im.svg  | fp : lib/icomoon/fonts/icomoon.svg
fonts/im.ttf  | fp : lib/icomoon/fonts/icomoon.ttf
fonts/im.woff | fp : lib/icomoon/fontsfonts/icomoon.woff

Gruntfile.coffee

module.exports = (grunt)->
  'use strict'

  _ = grunt.util._
  path = require 'path'

  grunt.initConfig

    pkg: grunt.file.readJSON('package.json')

    coffeelint:
      gruntfile:
        src: 'Gruntfile.coffee'
      application:
        src: ['scripts/**/*.coffee']
      options: grunt.file.readJSON('../coffeelint.json')

    coffee:
      options:
        sourceMap: true
        join: true
      index:
        src: [
          'scripts/public/*.coffee'
        ]
        dest: 'scripts/public/index.js'
      admin:
        src: [
          'scripts/admin/*.coffee'
        ]
        dest: 'scripts/admin/admin.js'

    recess:
      options:
        compile: true
      index:
        src: ['styles/index.less']
        dest: 'styles/index.css'
      admin:
        src: ['styles/admin.less']
        dest: 'styles/admin.css'
      productprint:
        src: ['styles/product-print.less']
        dest: 'styles/product-print.css'

    jade:
      options:
        client: true
      templates:
        src: ["templates/*.jade"]
        dest: "templates/templates.js"

    watch:
      options:
        atBegin: true
      scripts:
        files: 'scripts/**/*.coffee'
        tasks: ['coffeelint:application', 'coffee']
      templates:
        files: 'templates/*.jade'
        tasks: ['jade:templates']
      styles:
        files: 'styles/*.less'
        tasks: ['recess']

  grunt.loadNpmTasks 'grunt-coffeelint'
  grunt.loadNpmTasks 'grunt-contrib-coffee'
  grunt.loadNpmTasks 'grunt-contrib-clean'
  grunt.loadNpmTasks 'grunt-contrib-watch'
  grunt.loadNpmTasks 'grunt-contrib-jade'
  grunt.loadNpmTasks 'grunt-recess'

  # tasks.
  grunt.registerTask 'compile', [
    'coffeelint'
    'coffee'
  ]

  grunt.registerTask 'default', [
    'compile'
    'jade:templates'
    'recess'
  ]

It gave me the full power of Grunt for compiling stage (coffee, less, jade etc). And simple and predictable syntax to prepare assets for "client usage".

over 1 year ago ·

@mahnunchik - combining "full power of Grunt" with the "simple and predictable syntax" of ASPAX is an excellent idea.

Don't forget that you can also grunt.util.spawn aspax CLI in your scenario!

over 1 year ago ·

Have a fresh tip? Share with Coderwall community!

Post
Post a tip
Did you know?

JavaScript on a page runs with the privileges of that origin. One altered library, tag manager, or snippet can read inputs, change what people see, and call your APIs as the user.

Security insights from Cside