{"id":18389604,"url":"https://github.com/anseki/grunt-task-helper","last_synced_at":"2025-04-07T02:34:35.158Z","repository":{"id":9076877,"uuid":"10849895","full_name":"anseki/grunt-task-helper","owner":"anseki","description":"Help with handling the files (e.g. check changed files) before other tasks, or adding something (e.g. replace text).","archived":false,"fork":false,"pushed_at":"2022-08-19T03:10:46.000Z","size":28,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-22T11:45:04.349Z","etag":null,"topics":["changed-file","exclude","file-modification-time","file-size","filter","filter-files","grunt","grunt-plugins","gruntplugin","javascript","mtime","replace-text","taskhelper","updated-file"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anseki.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-06-21T18:51:06.000Z","updated_at":"2022-08-19T03:10:48.000Z","dependencies_parsed_at":"2022-09-10T09:01:48.241Z","dependency_job_id":null,"html_url":"https://github.com/anseki/grunt-task-helper","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fgrunt-task-helper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fgrunt-task-helper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fgrunt-task-helper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anseki%2Fgrunt-task-helper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anseki","download_url":"https://codeload.github.com/anseki/grunt-task-helper/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247583524,"owners_count":20962048,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["changed-file","exclude","file-modification-time","file-size","filter","filter-files","grunt","grunt-plugins","gruntplugin","javascript","mtime","replace-text","taskhelper","updated-file"],"created_at":"2024-11-06T01:43:56.809Z","updated_at":"2025-04-07T02:34:30.149Z","avatar_url":"https://github.com/anseki.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# grunt-task-helper\n\n[![npm](https://img.shields.io/npm/v/grunt-task-helper.svg)](https://www.npmjs.com/package/grunt-task-helper) [![GitHub issues](https://img.shields.io/github/issues/anseki/grunt-task-helper.svg)](https://github.com/anseki/grunt-task-helper/issues) [![dependencies](https://img.shields.io/badge/dependencies-No%20dependency-brightgreen.svg)](package.json) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\ntaskHelper helps selecting files for other tasks.  \nFor example, you want to minify only changed JS files. Then taskHelper selects files which are newer than `dest` from `src` (or newer than the time when this ran last time), and these files are passed to [grunt-contrib-uglify](https://github.com/gruntjs/grunt-contrib-uglify) task.\n\nAnd, taskHelper helps you do something small to files (or file's contents).  \nFor example, rename file, replace text, etc...  \nYou can create your custom task to do something easily via `grunt.registerTask()`. Or, writing new plugin is easy too. Using taskHelper is more easy.\n\n## Getting Started\nThis plugin requires Grunt `~0.4.1`\n\nIf you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:\n\n```shell\nnpm install grunt-task-helper --save-dev\n```\n\nOnce the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:\n\n```js\ngrunt.loadNpmTasks('grunt-task-helper');\n```\n\n## The \"taskHelper\" task\ntaskHelper accepts standard Grunt `files` components (see [Files](http://gruntjs.com/configuring-tasks#files)) and [*handlers*](#handlers).  \nThe handler is a *JavaScript Function* which you wrote, or a name of [builtin handler](#builtin-handlers). These handlers are called in some timings to select files or do something to files or file's contents. If you want, taskHelper creates standard Grunt `files` components which are new for other tasks.\n\n### Overview\nIn your project's Gruntfile, add a section named `taskHelper` to the data object passed into `grunt.initConfig()` (see [Configuring tasks](http://gruntjs.com/configuring-tasks)).  \nYou specify the `files` and some [*handlers*](#handlers) (e.g. `options.handlerByFile`). taskHelper accepts these all files, and some files are selected or done something via handler, and only selected files (filtered files) are passed to other tasks via `options.filesArray`.\n\n**Example:** Copy only CSS files which are needed. This handler works like expanded [Custom Filter Function](http://gruntjs.com/configuring-tasks#custom-filter-function).\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFileSrc: function(src, dest, options) {\n          var pathElms = src.match(/^(.+)\\.css$/), filepath;\n          if (pathElms) {\n\n            // CSS file from SCSS file is not needed.\n            if (grunt.file.exists(pathElms[1] + '.scss'))\n              { return false; }\n\n            // Give priority to souces directory.\n            filepath = src.replace(/^develop/, 'souces');\n            if (grunt.file.exists(filepath)) { return filepath; }\n          }\n        },\n        filesArray: []\n      },\n      src: 'develop/**/*.{css,scss}',\n      dest: 'backup/'\n    }\n  },\n  copy: {\n    deploy: {\n      // Copy files which are selected via taskHelper.\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n**Example:** Minify only changed JS files. (see [Builtin handler \"newFile\"](#builtin-handlers-newfile).)\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        // Select files which are newer than `dest`.\n        handlerByFile: 'newFile',\n        filesArray: []\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.js',\n      dest: 'public_html/'\n    }\n  },\n  uglify: {\n    deploy: {\n      // Minify files which are selected via taskHelper.\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n**Example:** Insert menu navigation into HTML files.\n\n`Gruntfile.js`\n\n```js\nvar htmlMenu = grunt.file.read('../menu.html');\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByContent: function(contentSrc, options) {\n          // Insert HTML content to placeholder.\n          return contentSrc.replace(/\u003c%MENU%\u003e/, htmlMenu);\n        }\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.html',\n      dest: 'public_html/'\n    }\n  }\n});\n```\n\n**Example:** Edit HTML files to refer to minified JS files (e.g. `file.js` to `file.min.js`).\n\n`Gruntfile.js`\n\n```js\nvar jsFiles = [];\ngrunt.initConfig({\n  taskHelper: {\n    // Get source JS files and minified JS files via globbing pattern.\n    getJs: {\n      options: {\n        filesArray: jsFiles\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.js',\n      dest: 'public_html/',\n      ext: '.min.js'\n    },\n    // Edit HTML files. \u003cscript src=\"file.js\"\u003e to \u003cscript src=\"file.min.js\"\u003e\n    editHtml: {\n      options: {\n        handlerByContent: function(contentSrc, options) {\n          jsFiles.forEach(function(f) {\n            // Ignore directory path, but not doing it is better.\n            var src = f.src[0].replace(/^.*\\//, ''),\n              dest = f.dest.replace(/^.*\\//, ''),\n              reSrc = new RegExp('(\u003cscript\\\\b[^\u003e]+\\\\bsrc=\"[^\"]*)' +\n                src.replace(/(\\W)/g, '\\\\$1') + '(\")', 'ig');\n              // Make these in every calling this function,\n              // but keeping these is better.\n            contentSrc = contentSrc.replace(reSrc, '$1' + dest + '$2');\n          });\n          return contentSrc;\n        }\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.html',\n      dest: 'public_html/'\n    }\n  },\n  uglify: {\n    minifyJs: {\n      // Minify files which are selected via taskHelper:getJs.\n      files: jsFiles\n    }\n  }\n});\n\n// taskHelper:getJs must be first.\ngrunt.registerTask('default',\n  ['taskHelper:getJs', 'uglify:minifyJs', 'taskHelper:editHtml']);\n```\n\n### \u003ca name =\"handlers\"\u003eHandlers\u003c/a\u003e\ntaskHelper parses `files` components, and calls handlers at some timings. The flow may be changed by return value of handlers.  \nYou can specify *JavaScript Function* which you wrote, or a name of [builtin handler](#builtin-handlers). If you want, you can specify multiple handlers into a timing by specifying array of these.\n\nBelow are timings which call handlers, and how to specify each handlers.\n\n#### handlerByTask\nThe handlers which were specified via `options.handlerByTask` are called per a task(target) before `files` components are parsed. This may be a handler, or an array which includes multiple handlers. (see [Cycle of handlers](#cycle-handlers).)  \nIf *JavaScript Function* is specified, following arguments are assigned, and return value is the following meaning.\n\n```js\nhandlerByTask(options)\n```\n\n+ \u003cstrong\u003e`options` Type: Object\u003c/strong\u003e  \nCopy of `options` which is passed to `grunt.initConfig()`.\n\n+ \u003cstrong\u003eReturn value\u003c/strong\u003e  \nIf the handler returns `false`, exit current task immediately, and the remaining handlers are not called.  \n*NOTE:* Any value except `false` (e.g. `undefined` or returns with *no value*) is ignored. i.e. it means the same as `return true`.\n\n**Example:**\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  // Configuring httpd.\n  concat: {\n    setup: {\n      src: ['/lib/httpd/common.conf', 'httpd/project.conf'],\n      dest: '/etc/httpd/conf/httpd.conf'\n    }\n  },\n  taskHelper: {\n    setup: {\n      options: {\n        handlerByTask: function(options) {\n          // Restart httpd.\n          var exec = require('child_process').exec;\n          exec('service node-httpd restart',\n            function (error, stdout, stderr) {\n              console.log('stdout: ' + stdout);\n              console.log('stderr: ' + stderr);\n              if (error !== null)\n                { console.log('exec error: ' + error); }\n            }\n          );\n        }\n      }\n    }\n  }\n});\n```\n\n#### handlerByFileSrc\nThe handlers which were specified via `options.handlerByFileSrc` are called per a `src` file in specified `files` components (and parsed by Grunt). This may be a handler, or an array which includes multiple handlers. (see [Cycle of handlers](#cycle-handlers).)  \nIf *JavaScript Function* is specified, following arguments are assigned, and return value is the following meaning.\n\n```js\nhandlerByFileSrc(src, dest, options)\n```\n\n+ \u003cstrong\u003e`src` Type: String\u003c/strong\u003e  \nThe file path of a `src` file in specified `files` components. This was parsed by Grunt, therefore [Globbing patterns](http://gruntjs.com/configuring-tasks#globbing-patterns) (e.g. `foo/*.js`) which was specified to `src` became real path that was found (e.g. `foo/file-1.js`).  \nIf `src` includes multiple files (e.g. array of files, or files `file-1.js` and `file-2.js` which was found via globbing pattern `*.js`), all handlers in `handlerByFileSrc` are called with an assigned first file in `src`, and these handlers are called with an assigned second file, and continue next. (see [Cycle of handlers](#cycle-handlers).) Note that this flow may be changed by handler. (see Return value below.)\n\n+ \u003cstrong\u003e`dest` Type: String\u003c/strong\u003e  \nThe file path of a `dest` file in specified `files` components.\n\n+ \u003cstrong\u003e`options` Type: Object\u003c/strong\u003e  \nCopy of `options` which is passed to `grunt.initConfig()`.\n\n+ \u003cstrong\u003eReturn value\u003c/strong\u003e  \nIf the handler returns `false`, remove current file path from `src`, and the remaining handlers in `handlerByFileSrc` are not called. This is like a `filter` (see [Custom Filter Function](http://gruntjs.com/configuring-tasks#custom-filter-function)). If other tasks will take these selected files (filtered files), an Array must be specified to `options.filesArray`. taskHelper adds `files` components to this array, and you let other task refer to this array.  \n*NOTE:* Any value except `false` (e.g. `undefined` or returns with *no value*) is ignored. i.e. it means the same as `return true`.  \nIf the handler returns String, the current `src` file path is replaced with this String. Note that other task may ignore the `src` file which doesn't exist.\n\n**Example:**\n\n`Gruntfile.js`\n\n```js\nvar cssFiles = [ {\n    src: 'theme-base/css/base.css',\n    dest: 'public_html/css/base.css'\n  } ],\n  path = require('path');\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFileSrc: function(src, dest, options) {\n          var pathElms = path.basename(src).match(/^(.+?)\\.test(\\.css)$/), newSrc;\n          if (pathElms) {\n            newSrc = 'theme-dark/css/' + pathElms[1] + pathElms[2];\n            return grunt.file.exists(newSrc) ?\n              newSrc :  // Change theme to dark.\n              false;    // Exclude this.\n          }\n        },\n        filesArray: cssFiles\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.css',\n      dest: 'public_html/'\n    }\n  },\n  cssmin: {\n    deploy: {\n      files: cssFiles\n    }\n  }\n});\n```\n\n#### handlerByFile\nThe handlers which were specified via `options.handlerByFile` are called per an element in specified `files` components (and parsed by Grunt, and might have changed by `handlerByFileSrc`). This may be a handler, or an array which includes multiple handlers. (see [Cycle of handlers](#cycle-handlers).)  \nIf *JavaScript Function* is specified, following arguments are assigned, and return value is the following meaning.\n\n```js\nhandlerByFile(srcArray, dest, options)\n```\n\n+ \u003cstrong\u003e`srcArray` Type: Array\u003c/strong\u003e  \nThe array which includes the file path of `src` files in specified `files` components. This was parsed by Grunt, therefore [Globbing patterns](http://gruntjs.com/configuring-tasks#globbing-patterns) (e.g. `foo/*.js`) which was specified to `src` became real path that was found (e.g. `foo/file-1.js`). And, these might have changed by handlers in `handlerByFileSrc`. (see above.)  \nIf `src` is a file path (e.g. String `'foo/file.js'`), this array includes an element (e.g. Array `['foo/file.js']`). If `src` files were all not found, array is empty.\n\n+ \u003cstrong\u003e`dest` Type: String\u003c/strong\u003e  \nThe file path of a `dest` file in specified `files` components.\n\n+ \u003cstrong\u003e`options` Type: Object\u003c/strong\u003e  \nCopy of `options` which is passed to `grunt.initConfig()`.\n\n+ \u003cstrong\u003eReturn value\u003c/strong\u003e  \nIf the handler returns `false`, remove current element from `files` components, and the remaining handlers in `handlerByFile` are not called. If other tasks will take these selected files (filtered files), an Array must be specified to `options.filesArray`. taskHelper adds `files` components to this array, and you let other task refer to this array.  \n*NOTE:* Any value except `false` (e.g. `undefined` or returns with *no value*) is ignored. i.e. it means the same as `return true`.  \nIf the handler returns String, the current `dest` file path is replaced with this String.\n\n**Example:**\n\n`Gruntfile.js`\n\n```js\nvar path = require('path');\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: function(srcArray, dest, options) {\n          if (!srcArray.length) {\n            // Exclude this.\n            return false;\n          } else if (srcArray.length \u003e 1) {\n            // Multiple photos in a page.\n            return path.dirname(dest) + '/thumbnails.html';\n          }\n        },\n        filesArray: []\n      },\n      files: [\n        {\n          src: 'public_html/jon/photos/*.jpg',\n          dest: 'public_html/jon/photos/photo.html'\n        },\n        {\n          src: 'public_html/kim/photos/*.jpg',\n          dest: 'public_html/kim/photos/photo.html'\n        }\n      ]\n    }\n  },\n  makeGallery: {\n    deploy: {\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n#### handlerByContent\nThe handlers which were specified via `options.handlerByContent` are called per a `dest` file in specified `files` components (and parsed by Grunt, and might have changed by `handlerByFileSrc`). This may be a handler, or an array which includes multiple handlers. (see [Cycle of handlers](#cycle-handlers).)  \nThese are handlers to edit (or check) the content that should be written to `dest` file.  \nIf *JavaScript Function* is specified, following arguments are assigned, and return value is the following meaning.\n\n```js\nhandlerByContent(contentSrc, options)\n```\n\n+ \u003cstrong\u003e`contentSrc` Type: String\u003c/strong\u003e  \nThe current `src` file's contents to first handler. To remaining handlers, the text which returned by previous handler.  \nAll current `src` files (which were parsed by Grunt, and might have changed by `handlerByFileSrc`) are loaded, and these concatenated contents is passed to first handler. The text which was returned via the end handler is written to `dest` file.\n\n+ \u003cstrong\u003e`options` Type: Object\u003c/strong\u003e  \nCopy of `options` which is passed to `grunt.initConfig()`.\n\n+ \u003cstrong\u003eReturn value\u003c/strong\u003e  \nThis value is passed to next handler. The end handler returns value to write to `dest` file.  \nIf the handler returns `false`, the remaining handlers in `handlerByContent` are not called, and `dest` file is done nothing.  \n*NOTE:* Any value except `false` (e.g. `undefined` or returns with *no value*) is content. i.e. if nothing was returned, empty text is written to `dest` file.\n\n##### options.separator\nAll contents of current `src` files are concatenated with `options.separator`. For example, *line-break-character* `\\n` can be specified.  \nIf this option was not specified, taskHelper uses *line-break-character* which was found first in current `src` file's contents. If that was not found, `grunt.util.linefeed` is used.\n\nPriority Order:\n\n1. `options.separator`\n2. line-break-character in `src` file's contents (found first)\n3. `grunt.util.linefeed`\n\n*NOTE:* `grunt.util.linefeed` is chosen via operating system which executes this task. Not operating system which uses the generated files. If this task is executed on Windows, `\\n` for others will be specified to `options.separator` or `src` files may include this.\n\n**Example:**\n\n`Gruntfile.js`\n\n```js\nvar htmlMenu = grunt.file.read('../menu.html');\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByContent: [\n          // 1st handler\n          function(contentSrc, options) {\n            // Insert HTML content to placeholder.\n            return contentSrc.replace(/\u003c%MENU%\u003e/, htmlMenu);\n          },\n          // End handler\n          function(contentSrc, options) {\n            var pathElms = contentSrc.match(/\u003ch2\\b.*?\u003e(.+?)\u003c\\/h2\u003e/), pageTitle;\n            if (pathElms) {\n              // Content title\n              pageTitle = pathElms[1];\n              // Style menu item of current page.\n              // (a part of HTML inserted by 1st handler)\n              return contentSrc.replace(\n                new RegExp('\u003cli class=\"menu-item\"\u003e(?=' + pageTitle + ')'),\n                '\u003cli class=\"menu-item current\"\u003e');\n            } else return contentSrc;\n          }\n        ]\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.html',\n      dest: 'public_html/'\n    }\n  }\n});\n```\n\n#### handlerByAllFiles\nThe handlers which were specified via `options.handlerByAllFiles` are called per a task(target) after `files` components are parsed (and might have changed by `handlerByFileSrc` and `handlerByFile`). This may be a handler, or an array which includes multiple handlers. (see [Cycle of handlers](#cycle-handlers).)  \nIf *JavaScript Function* is specified, following arguments are assigned, and return value is the following meaning.\n\n```js\nhandlerByAllFiles(files, options)\n```\n\n+ \u003cstrong\u003e`files` Type: Array\u003c/strong\u003e  \nThis is specified `files` components. This was parsed by Grunt, therefore [Globbing patterns](http://gruntjs.com/configuring-tasks#globbing-patterns) (e.g. `foo/*.js`) which was specified to `src` became real path that was found (e.g. `foo/file-1.js`). And, these might have changed by handlers in `handlerByFileSrc` and `handlerByFile`. (see above.)  \nIf the files were all not found or these were all removed by `handlerByFileSrc` and `handlerByFile`, array is empty.  \nThis array may be different from `options.filesArray`. This array includes only files which were selected in current task(target), but the `options.filesArray` may include files which were added by other tasks(targets) too.\n\n+ \u003cstrong\u003e`options` Type: Object\u003c/strong\u003e  \nCopy of `options` which is passed to `grunt.initConfig()`.\n\n+ \u003cstrong\u003eReturn value\u003c/strong\u003e  \nIf the handler returns `false`, exit current task immediately, and the remaining handlers are not called.  \n*NOTE:* Any value except `false` (e.g. `undefined` or returns with *no value*) is ignored. i.e. it means the same as `return true`.\n\n**Example:**\n\n`Gruntfile.js`\n\n```js\nvar path = require('path');\ngrunt.initConfig({\n  taskHelper: {\n    pack: {\n      options: {\n        // Concatenate all CSS into all.css, and all JS into all.js\n        handlerByContent: function(content) { return content; },\n        // Pack concatenated files into assets.zip\n        handlerByAllFiles: function(files, options){\n          var exec = require('child_process').exec;\n          exec('zip assets.zip ' +\n            files.map(function(f) { return f.dest; }).join(' '),\n            function (error) {\n              if (error !== null)\n                { console.log('exec error: ' + error); }\n            }\n          );\n        },\n      },\n      files: [\n        {\n          src: 'css/*.css',\n          dest: 'assets/all.css'\n        },\n        {\n          src: 'js/*.js',\n          dest: 'assets/all.js'\n        }\n      ]\n    }\n  }\n});\n```\n\n### \u003ca name =\"cycle-handlers\"\u003eCycle of handlers\u003c/a\u003e\nThe handlers are called at some timings as follows unless it is aborted by returning false.\n\n```\n---------------------------------- \u003cTask 1\u003e\n|  CALL handlerByTask 1\n|  CALL handlerByTask 2\n|    :\n|\n|  ------------------------------- \u003cElement 1 of files\u003e\n|  |\n|  |  ---------------------------- \u003csrc file 1\u003e\n|  |  |  CALL handlerByFileSrc 1\n|  |  |  CALL handlerByFileSrc 2\n|  |  |    :\n|  |  ----------------------------\n|  |\n|  |  ---------------------------- \u003csrc file 2\u003e\n|  |  |  CALL handlerByFileSrc 1\n|  |  |  CALL handlerByFileSrc 2\n|  |  |    :\n|  |  ----------------------------\n|  |    :\n|  |\n|  |  CALL handlerByFile 1\n|  |  CALL handlerByFile 2\n|  |    :\n|  |\n|  |  CALL handlerByContent 1\n|  |  CALL handlerByContent 2\n|  |    :\n|  -------------------------------\n|\n|  ------------------------------- \u003cElement 2 of files\u003e\n|  |  (Same as \u003cElement 1 of files\u003e)\n|  -------------------------------\n|    :\n|\n|  CALL handlerByAllFiles 1\n|  CALL handlerByAllFiles 2\n|    :\n----------------------------------\n\n---------------------------------- \u003cTask 2\u003e\n|  (Same as \u003cTask 1\u003e)\n----------------------------------\n  :\n```\n\n### \u003ca name =\"builtin-handlers\"\u003eBuiltin handlers\u003c/a\u003e\nYou can specify name of builtin handler instead of JavaScript Function.  \nNow, taskHelper has following builtin handlers.\n\n#### \u003ca name =\"builtin-handlers-newfile\"\u003enewFile\u003c/a\u003e\nThis is a handler for `handlerByFile` to select files which are newer than `dest` from `src` (or newer than the time when this ran last time).\n\n+ In the basic case, `srcArray` includes a file path and a `dest` file path was specified. The modification times of both files are compared, and if `src` file is newer than `dest` file, return true.  \n**One src file : One dest file**\n\n**Example:** Minify only changed JS files.\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: 'newFile',\n        filesArray: []\n      },\n      files: [\n        {\n          src: 'develop/js/a.js',\n          dest: 'public_html/js/a.min.js'\n        },\n        {\n          src: 'develop/js/b.js',\n          dest: 'public_html/js/b.min.js'\n        }\n      ]\n    }\n  },\n  uglify: {\n    deploy: {\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n+ When the `srcArray` includes multiple files path and a `dest` file path was specified, if there is any `src` file in which is newer than `dest` file, return true.  \n**Some src files : One dest file**\n\n**Example:** Concatenate files if one or more source files were updated. (More better way in [Tips](#tips))\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: 'newFile',\n        filesArray: []\n      },\n      src: 'develop/css/*.css',\n      dest: 'public_html/css/all.css'\n    }\n  },\n  concat: {\n    deploy: {\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n+ When the file to compare was not specified (e.g. `dest` was not specified, or `dest` is same file as `src`), if `src` file is newer than the time when this ran last time, return true.  \ntaskHelper saves log file `.grunt/grunt-task-helper/fileUpdates.json` for compare.  \n**One or more src files : No dest file**\n\n**Example:** You don't need to keep PNG files that is source of minifying, because you have PSD files which is editable always and outputable to PNG.\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: 'newFile',\n        filesArray: []\n      },\n      expand: true,\n      src: 'public_html/img/*.png'\n    }\n  },\n  imagemin: {\n    deploy: {\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n*NOTE:* Logging the modification time of the file after task is best way, but current Grunt can't do it. Therefore the time is got via using `options.mtimeOffset`. (see source code.)\n\n#### \u003ca name =\"builtin-handlers-size\"\u003esize\u003c/a\u003e\nThis is a handler for `handlerByFileSrc` to select files which match specified size.  \nYou can specify minimum file size to `options.minSize`, and maximum file size to `options.maxSize`. One or both of these must be specified.\n\n**Example:** Compress only big files.\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFileSrc: 'size',\n        minSize: 1024 * 64,\n        filesArray: []\n      },\n      expand: true,\n      src: 'public_html/download/*.csv',\n    }\n  },\n  compress: {\n    options: {\n      mode: 'gzip'\n    },\n    deploy: {\n      files: '\u003c%= taskHelper.deploy.options.filesArray %\u003e'\n    }\n  }\n});\n```\n\n## \u003ca name =\"tips\"\u003eTips\u003c/a\u003e\n\n### Task efficiency\nSome grunt plugins are wrapper of something which provide a method. And the method accepts `src` file's contents and returns contents to write to `dest` file.  \nIf you want to give selected files by taskHelper to the plugin, specifying the method into `handlerByContent` instead of the using the plugin is better. Because grunt parses `files` components in every tasks(targets). `handlerByContent` can be included to one task with other handlers.\n\n**Example:** Minify CSS files if one or more source files were updated.  \n[clean-css](https://github.com/GoalSmashers/clean-css) is wrapped by [grunt-contrib-cssmin](https://github.com/gruntjs/grunt-contrib-cssmin) plugin.\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    cssmin: {\n      options: {\n        handlerByFile: 'newFile',\n        handlerByContent: (new require('clean-css')({relativeTo: 'public_html/css'})).minify,\n      },\n      src: 'develop/css/*.css',\n      dest: 'public_html/css/all.css'\n    }\n  }\n});\n```\n\n**Example:** Minify only changed HTML files.  \n[htmlclean](https://github.com/anseki/htmlclean) is wrapped by [grunt-htmlclean](https://github.com/anseki/grunt-htmlclean) plugin.\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: 'newFile',\n        handlerByContent: require('htmlclean')\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.html',\n      dest: 'public_html/'\n    }\n  }\n});\n```\n\n**Example:** Concatenate files if one or more source files were updated.  \n`handlerByContent` works like [grunt-contrib-concat](https://github.com/gruntjs/grunt-contrib-concat) plugin. (Another plugin or module is unneeded.)\n\n`Gruntfile.js`\n\n```js\ngrunt.initConfig({\n  taskHelper: {\n    deploy: {\n      options: {\n        handlerByFile: 'newFile',\n        handlerByContent: function(content) { return content; }, // Do nothing.\n        separator: ';'\n      },\n      src: 'develop/js/*.js',\n      dest: 'public_html/js/all.js'\n    }\n  }\n});\n```\n\n### Log messages\nGrunt outputs log to STDOUT. Handlers can output messages.  \n\n**Example:** When there are a lot of targets, handlers output messages outstanding more than message `Running \"...\" task` by Grunt.\n\n`Gruntfile.js`\n\n```js\nvar head = '==================================== ';\ngrunt.initConfig({\n  taskHelper: {\n    options: {\n      handlerByTask: function()\n        { grunt.log.writeln((head + grunt.task.current.target.bold).cyan); }\n    },\n    // Even if there is not work, a {} is necessary.\n    html: { /* Do something if needed */ },\n    css:  { /* Do something if needed */ },\n    js:   { /* Do something if needed */ },\n    img:  { /* Do something if needed */ }\n  },\n  fooTask: {\n    html: { /* Do something */ },\n    css:  { /* Do something */ },\n    js:   { /* Do something */ },\n    img:  { /* Do something */ }\n  },\n  barTask: {\n    html: { /* Do something */ },\n    css:  { /* Do something */ },\n    //js:   { /* Do something */ },\n    img:  { /* Do something */ }\n  }\n});\ngrunt.registerTask('default', [\n  'taskHelper:html', 'fooTask:html', 'barTask:html',\n  'taskHelper:css', 'fooTask:css', 'barTask:css',\n  'taskHelper:js', 'fooTask:js',\n  'taskHelper:img', 'fooTask:img', 'barTask:img'\n]);\n```\n\n### \"Continue?\", \"Overwrite?\" - Wizard style\nThe handlers work via user's response for interactively running.\n\n**Example:**  Interactively running via [readlineSync](https://github.com/anseki/readline-sync).  \nInstall readlineSync.\n\n```shell\nnpm install readline-sync\n```\n\n`Gruntfile.js`\n\n```js\nvar readlineSync = require('readline-sync');\ngrunt.initConfig({\n  taskHelper: {\n    confirm: {\n      options: {\n        handlerByTask: function() {\n          // Abort the task if user don't want.\n          return readlineSync.question('The HTML files are copied. Continue? :')\n            .toLowerCase() === 'y';\n          // Or process.exit()\n        },\n        handlerByFile: function(srcArray, dest) {\n          if (grunt.file.exists(dest)) {\n            // Exclude this file if user want to keep it.\n            return readlineSync.question('This file already exists.\\n' + dest + '\\n' +\n              'Overwrite this file? :').toLowerCase() === 'y';\n          }\n        },\n        filesArray: []\n      },\n      expand: true,\n      cwd: 'develop/',\n      src: '**/*.html',\n      dest: 'public_html/'\n    }\n  },\n  copy: {\n    confirm: {\n      files: '\u003c%= taskHelper.confirm.options.filesArray %\u003e'\n    }\n  }\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanseki%2Fgrunt-task-helper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanseki%2Fgrunt-task-helper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanseki%2Fgrunt-task-helper/lists"}