/grunt-ez-frontend

Easy configuration for grunt to create concatenated/minified versions of css/less and js files

Primary LanguageJavaScriptMIT LicenseMIT

grunt-ez-frontend

Easy define the css, less files to be parsed, concatenated, autoprefixed and minified, as well as the js files which will be concatenated and minified.

The main task is ez-frontend this is a multitask that will make it easy to configure which files will produce which output. Based on the following convention:

a group of less, css files will generate two css outputs, one with the concatenation of the css files and the autoprefixing of the css properties (using grunt-autoprefixer) and other that is the result of applying grunt-csso to the previous output. Also the assets referenced by the stylesheet will be moved to be relative to the location of the output.

The previous means that a group like the one below:

var cssGroup = {
   src : ['path/to/some/less-file.less', 'path/to/other.less', 'path/to/a/css.file.css'],
   dest : 'path/to/some/output/dir/output.css'
}

Will generate a file path/to/some/output/dir/output.css by first parse the less files and concatenate them with the css files (if any) and then apply autoprefixing to the css properties that require it. Also a file path/to/some/output/dir/output.min.css will be created. This a standard practice to add .min to a file before the extension to denote that the file is intended to be used in production.

Also a group of js files will generate 2 outputs, one concatenated and preprocessed to include template files as inline strings, and other optimized using grunt-uglify.

The previous means that a group like:

var jsGroup = {
   src : ['path/to/some/js-file.js', 'path/to/other.js', 'path/to/a/js.file.js'],
   dest : 'path/to/some/output/dir/output.js'
}

Will generate a file path/to/some/output/dir/output.js which is going to be the result of the preprocess task and a file path/to/some/output/dir/output.min.js that will be the result of applying grunt-uglify to the previous generated file.

This module provide two multi tasks appart from the ez-frontend multitask:

  • preprocess, a tasks that it is similar to grunt-contrib-concat, but that appart from only concatenate the files, it also allows for some pre-processing and post-processing of the file. It can perform replacement of tokens and include of HTML templates into the javascript to avoid having multiline text on the javascript file and to avoid having to put the templates in the html and then querying from there. Note: This is useful when is required to compile the templates on the fly on the browser. If you don't need to compile the templates on the fly maybe it will be better to compile them during the building process.

  • cLess, a tiny utility to parse less files, and concatenate them along with regular css files. This utility also move all the assets referenced to a location relative to the generated css file, during this process it also apply revving to the assets, the output path of the assets could be customized to suit different needs.

Getting Started

Install this grunt plugin next to your project's Gruntfile.js gruntfile with: npm install grunt-ez-frontend --save-dev

Since this module depends on grunt-uglify, grunt-autoprefixer and grunt-csso, you will need to install them too: Execute npm install grunt-uglify --save-dev, npm install grunt-autoprefixer --save-dev, npm install grunt-csso --save-dev

Then add this line to your project's grunt.js gruntfile:

grunt.loadNpmTasks("grunt-ez-frontend");
// don't forget to add the other dependencies too
grunt.loadNpmTasks("grunt-uglify");
grunt.loadNpmTasks("grunt-csso");
grunt.loadNpmTasks("grunt-autoprefixer");

Documentation

The ez-frontend tasks is a multi task, meaning that grunt will automatically iterate over all the targets under this task configuration object

ez-frontend

Target Properties

  • src(required): Depending on the type of output to generate this could be: a. The LESS file(s) to be compiled. Can be either a string or an array of strings. If more than one LESS file is provided, each LESS file will be compiled individually and then concatenated together. You can mix regular css files with the less files, they will be just added since they don't need to be parsed. or...

    b. The JS files to be concatenated and compiled.

  • dest(required): The path where the output from the LESS compilation or preprocess should be placed. Must be a string as there can be only one destination. The extension of the file is important as this is used to infer the type of the group. You can also specify a type for the group (either js or css), see the next property:

  • type(optional): wheter this group is js target or a css target. If ommited the type for the group will be infered from the extension of the output file.

  • options(optional): An object with the options to pass to the preprocess, uglify in case of a js group, and cLess, autoprefix, and csso tasks in case of a css group.

Example

// project configuration
grunt.initConfig({
  'ez-frontend': {
    // all the options here could be overridden at a target level
    // or using the the specific options for a particular task
    options: {

      cLessOptions: {},         // options for `cLess` tasks
      autoprefixerOptions: {},  // options for `autoprefixer` tasks
      cssoOptions: {},          // options for `csso` tasks
      preprocessOptions: {},    // options for `preprocess` tasks
      uglifyOptions: {},        // options for `uglify` tasks

      // the banner to be added to the top of the output files. 
      // It will be processed by grunt.template.process
      // so it could use the same format as the meta.banner property.
      banner: bannerContent, 
      
      // called before a less file is parsed
      // it will also receive css files
      // at this stage, the urls of the files were not modified
      // (they are modified to copy the referenced assets to a location
      // relative to the css output file
      beforeParseLess : function (content, filepath) {
        // do some operations here...
        // like generating some less instructions on the fly
        // that are going to be then transformed to css by the less parser
        // if the file was a less one
        return content;
      },
      
      // this imports will be added before the parsed less files
      // this is handy to add custom imports and mixins that you want 
      // to have on all your code
      // Be aware that if you plan to generate several targets 
      // and they don't share the same common files
      // this should be better set on the group level 
      customImports : [ CSS_CODE_DIR + 'common.less', CSS_CODE_DIR + 'font.less' ],

      // Add custom functions to be used as native "less css" functions.
      // 
      // The keys of the object will become the name of the "custom functions" 
      // 
      // The functions will be added to the less.tree.functions object
      // 
      // When called the function will receive the less object as the first parameter
      // and whatever other values were passed to the function from the less usage
      //
      // Example:
      // 
      // Using the 'em-calc' function in the less file
      //
      // font-size : em-calc(16px);
      //
      // will result in 
      // 
      // font-size : 1em /* em-calc output*/;
      //  
      // 
      userFunctions : {
        'em-calc' : function (unit /* is a less object that wraps the unit */) {
          var number = unit.value;
          if (!isNaN(number)) {
            return lib.format('{0}em /* em-calc output*/', number / 16);
          }
          throw new Error("em-calc expects a number!");
        }
      },
      
      // the this context object for the userFunctions, if not specified or null
      // the less object will be used
      userFunctionsThisObj : null,

      // if provided the filenames will be modified to use this 
      // as part of the file name, just before the extension.
      // For example : 
      // 
      // instead of 'path/to/some/output.css' it will be 
      // 'path/to/some/output.{version}.css' where {version} will be
      // replaced with the assetsVersion given.
      // 
      // Same will happen with the minified version in that case:
      // 
      // instead of 'path/to/some/output.min.css' it will be 
      // 'path/to/some/output.{version}.min.css' where {version} will be
      // replaced with the assetsVersion given.
      // 
      // also assets referenced by less files will be moved into a folder
      // with the version as the name. Please note this behavior could 
      // be overriden by setting a different rewritePathTemplate option
      assetsVersion : assetsVersion, 
      
      // this controls the format of the rewriten url for the assets
      // referenced in the less/css files. 
      // {0} will be replaced by the assetsVersion
      // {1} will be replaced by the md5 of the referenced url
      // {2} will be the original name of the asset
      // Another template could be 
      //   'assets/{1}_{2}'
      // In this case we're not using the assetsVersion 
      rewritePathTemplate : 'assets/{0}/{1}/{2}',

      // runs per each file (css, less or js file)
      // this hook allows to modify the content of the file being parsed
      // this will run for preprocess and cLess tasks. 
      // 
      // If this function is intended to run only for a particular group (css or js)
      // It will be better to use the options object of the target 
      // instead of this global hook.
      processContent: function(content, filePath) { 
        return content;
      },
      
      // runs after preprocess or cLess tasks are finished and are about 
      // to save the output to a disk.
      // This hook could be used to further modify the output if necessary.
      // 
      // If this function is intended to run only for a particular group (css or js)
      // It will be better to use the options object of the target 
      // instead of this global hook.
      postProcess: function(content, filePath) { 
        return content;
      }, 
      
      // this option is for the autoprefixer see grunt-autoprefixer for more info visit https://github.com/nDmitry/grunt-autoprefixer
      browsers: ['last 2 version', 'ie 8', 'ie 7'], 
      
      // this option will be passed to the preprocess task
      replacements : [{
        replace : /\[APP_VERSION\]/g,
        using : function () {
          return pkg.version;
        }, {
          replace : /\[AUTOR]/g,
          using : 'Roy Riojas'
        }
      }]
    },
    'app-js': {
      
      // This group will generate the files dist/main.js and dist/main.min.js 
      // also the files will be preprocess to replace some sections
      // with a comment that will be later be removed by uglify 
      // in this case the section to be replaced is a block of code only required
      // for user testing
      src: ['main.js', 'views/**/*.js'],
      dest: 'dist/main.js',
      options: {
        replacements: [{
          //remove code that is only for testing purposes and which is inside the editor-fold region
          replace: /\/\/<editor-fold desc="test-region">[\s\S.]*\/\/<\/editor-fold>/, 
          // could be a string or a function that returns a string
          using: '//CODE FOR TESTING REMOVED' 
        }]
      }
    },
    'app-css': {
      // This group will generate the main.css file and will replace
      // em-calc(number) with the em-value resulting of dividing the number by 16.
      // This could be used to create custom functions on the css
      src: ['main.less', 'other.css'],
      dest: 'dist/main.css',
      options: {
        // overrides the specified in the global options for the ez-frontend multitask
        postProcess: function(content) { 
          var regexEmCalc = /\bem-calc\((.*)\)/g;

          content = content.replace(regexEmCalc, function(a, b) {
            var number = parseFloat(b);
            if (!isNaN(number)) {
              return lib.format('{0}em', number / 16);
            }
            throw new Error("em-calc expects a number!");
          });

          return content;
        }
      }
    }
  }
})

Release History

  • 02/26/2014 - 0.2.16:

    • Added a way to send specific options per task.

      • cLessOptions: options for cLess tasks
      • autoprefixerOptions: options for autoprefixer tasks
      • cssoOptions: options for csso tasks
      • preprocessOptions: options for preprocess tasks
      • uglifyOptions: options for uglify tasks
    • Added two helper tasks to run css-targets and js-targets

      • css-target. Executes ez-frontend and cLess autoprefixer and csso for a given ez-frontend entry. To use it simply run grunt css-target:nameOfTarget' and the ez-frontend task will be executed, then the tasks cLess:nameOfTarget, autoprefixer:nameOfTargetandcsso:nameOfTarget` task are executed too
      • js-target. Executes ez-frontend and preprocess and uglify for a given ez-frontend entry. To use it simply run grunt js-target:nameOfTarget' and the ez-frontend task will be executed and then the preprocess:nameOfTargetand theuglify:nameOfTarget` will be executed.
  • 02/18/2014 - 0.2.15:

    • Added single task test to run generated qunit tests in isolation
    // syntax
    // grunt test:nameOfTask
    
    // will only execute the tests for coalesce instead of running all the defined tasks
    grunt test:coalesce
    
    
    • Custom user functions for less added. calc-em, calc-rem, rel-val. To use them just do:
    // syntax
    //
    // calc-rem(valueInPx[, baseFontSize]): return value in 'rems'
    // calc-em(valueInPx[, baseFontSize]): return value in 'ems'
    // rel-val(valueInPx[, relativeDimension]): return value relative to the relativeDimension passed in percentage
    
    // Examples
    
    font-size: calc-em(15px) // will output font-size: 1em (the default base font is 15px by default)
    font-size: calc-em(12px, 16) // will output font-size: 0.75em
    
    font-size: calc-rem(15px) // will output font-size: 1rem (the default base font is 15px by default)
    font-size: calc-rem(12px, 16) // will output font-size: 0.75rem
    
    width: rel-val(500, 1000) // will output width: 50%;
    
    
  • 02/16/2014 - 0.2.14:

    • Added a task to generate grunt-qunit-istanbul targets on the fly. It requires you have installed grunt-qunit-instanbul from this [fork][grunt-qunit-istanbul] in order to prevent this bug

    IMPORTANT: For this task to work, your qunit tests should be located inside folders with different unique names, since the name of the test is inferred from the name of the folder that contain the test. Also add the attribute data-cover to the scripts you want to get coverage from. This task will get the list of files where the data-cover attribute was present and create a target for grunt-qunit-istanbul

    The task could be used by adding the following task configuration in your grunt config

    'test-targets-generator': {
      options: {
        '--web-security': 'no',
        coverage: {
          instrumentedFiles: 'temp/',
          reportFolder: 'report/', //where to store the reports. A folder with the name of the target will be created inside
          linesThresholdPct: 85,
          statementsThresholdPct: 85,
          functionsThresholdPct: 85,
          branchesThresholdPct: 85
        }
      },
      all: {
        files: [ {
          src: [ 'tests/**/*.html' ],
          expand: true
        } ]
      }
    }
    

    Then simply you can type this in your command line to execute the qunit targets created.

    grunt test-targets-generator qunit
    
  • 02/10/2014 - 0.2.13:

    • Added logs while templates are created.
  • 02/10/2014 - 0.2.12:

    • Bug fix. Fixed default template to create compiled templates. The token to be replace was [CONTENT] and it was supposed to be [[CONTENT]].
  • 02/05/2014 - 0.2.10:

    • Bug fix. Added Fix to cLess to avoid corrupting font or other binary files while copying them to the assets location. Read more
  • 01/19/2014 - 0.2.9:

    • Bug fix. Banner were ignored in css outputs
  • 01/10/2014 - 0.2.8:

    • Added generic regular expressions replacements to the gruntTaskUtil.beautifier helper object. TODO: this will be moved to grunt-jsbeautifier After beautification the following replacements are made:
    replacements : [{
        replace : /!!\s/g, // double bang with one space after
        using : '!!' // removes the extra space at the end leaving only the double bang.
      }, {
        replace : /\(\sfunction/g, // extra space in anonymous functions passed as arguments
        using : '(function' // removes the extra space
      }, {
        replace : /\)\s\)/g,  // closing parenthesis with an space in the middle
        using : '))' // remove the middle space
      }]
  • 12/16/2013 - 0.2.7:

    • Bug Fix. Make the i18n-2-ez-frontend task to work as expected
  • 12/16/2013 - 0.2.6:

    • Added helper functions onBeautified and onVerificationFailed.
  • 12/16/2013 - 0.2.5:

    • Added bannerFile property to ez-frontend. When especified the banner property is replaced by the contents of the file mentioned by bannerFile
  • 12/16/2013 - 0.2.4:

    • Renamed fileTemplate property of compile-templates task to template property, also added a property to read the fileTemplate from a file in the filesystem.
  • 12/16/2013 - 0.2.3:

    • Expose some utilities as multitasks 'validate-templates', 'compile-templates', 'i18n-from-yml' and 'i18n-to-ez-frontend' also expose the gruntTaskUtil.registerTasks to pass an object with tasks that can be registered with grunt in a DRY way
  • 10/28/2013 - 0.2.2:

    • Add log message to cLess and preprocess tasks.
  • 10/28/2013 - 0.2.1:

    • Fix issue with new version of Less. Now using fixed semvers for dependencies.
  • 09/27/2013 - 0.1.8:

    • Make custom functions to work when assigned to variables.
  • 09/25/2013 - 0.1.7:

    • Added a customImports option to the cLess task to allow easy import of mixins and common files. options.customImports.
  • 09/25/2013 - 0.1.6:

    • Added support for userFunctions in less.
    • Added the current filename being processed for easier debugging of less errors.
    • Added a pre parse callback for less files options.beforeParseLess.
  • 09/21/2013 - 0.1.4: Initial release.

License

Copyright (c) 2013 Roy Riojas This module is based on the grunt-r3m module. Basically it has been rewritten to support the features I needed.

Licensed under the MIT license.