template.js 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /*
  2. * grunt
  3. * http://gruntjs.com/
  4. *
  5. * Copyright (c) 2014 "Cowboy" Ben Alman
  6. * Licensed under the MIT license.
  7. * https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
  8. */
  9. 'use strict';
  10. var grunt = require('../grunt');
  11. // The module to be exported.
  12. var template = module.exports = {};
  13. // External libs.
  14. template.date = require('dateformat');
  15. // Format today's date.
  16. template.today = function(format) {
  17. return template.date(new Date(), format);
  18. };
  19. // Template delimiters.
  20. var allDelimiters = {};
  21. // Initialize template delimiters.
  22. template.addDelimiters = function(name, opener, closer) {
  23. var delimiters = allDelimiters[name] = {};
  24. // Used by grunt.
  25. delimiters.opener = opener;
  26. delimiters.closer = closer;
  27. // Generate RegExp patterns dynamically.
  28. var a = delimiters.opener.replace(/(.)/g, '\\$1');
  29. var b = '([\\s\\S]+?)' + delimiters.closer.replace(/(.)/g, '\\$1');
  30. // Used by Lo-Dash.
  31. delimiters.lodash = {
  32. evaluate: new RegExp(a + b, 'g'),
  33. interpolate: new RegExp(a + '=' + b, 'g'),
  34. escape: new RegExp(a + '-' + b, 'g')
  35. };
  36. };
  37. // The underscore default template syntax should be a pretty sane default for
  38. // the config system.
  39. template.addDelimiters('config', '<%', '%>');
  40. // Set Lo-Dash template delimiters.
  41. template.setDelimiters = function(name) {
  42. // Get the appropriate delimiters.
  43. var delimiters = allDelimiters[name in allDelimiters ? name : 'config'];
  44. // Tell Lo-Dash which delimiters to use.
  45. grunt.util._.templateSettings = delimiters.lodash;
  46. // Return the delimiters.
  47. return delimiters;
  48. };
  49. // Process template + data with Lo-Dash.
  50. template.process = function(tmpl, options) {
  51. if (!options) { options = {}; }
  52. // Set delimiters, and get a opening match character.
  53. var delimiters = template.setDelimiters(options.delimiters);
  54. // Clone data, initializing to config data or empty object if omitted.
  55. var data = Object.create(options.data || grunt.config.data || {});
  56. // Expose grunt so that grunt utilities can be accessed, but only if it
  57. // doesn't conflict with an existing .grunt property.
  58. if (!('grunt' in data)) { data.grunt = grunt; }
  59. // Keep track of last change.
  60. var last = tmpl;
  61. try {
  62. // As long as tmpl contains template tags, render it and get the result,
  63. // otherwise just use the template string.
  64. while (tmpl.indexOf(delimiters.opener) >= 0) {
  65. tmpl = grunt.util._.template(tmpl, data);
  66. // Abort if template didn't change - nothing left to process!
  67. if (tmpl === last) { break; }
  68. last = tmpl;
  69. }
  70. } catch (e) {
  71. // In upgrading to Lo-Dash (or Underscore.js 1.3.3), \n and \r in template
  72. // tags now causes an exception to be thrown. Warn the user why this is
  73. // happening. https://github.com/documentcloud/underscore/issues/553
  74. if (String(e) === 'SyntaxError: Unexpected token ILLEGAL' && /\n|\r/.test(tmpl)) {
  75. grunt.log.errorlns('A special character was detected in this template. ' +
  76. 'Inside template tags, the \\n and \\r special characters must be ' +
  77. 'escaped as \\\\n and \\\\r. (grunt 0.4.0+)');
  78. }
  79. // Slightly better error message.
  80. e.message = 'An error occurred while processing a template (' + e.message + ').';
  81. grunt.warn(e, grunt.fail.code.TEMPLATE_ERROR);
  82. }
  83. // Normalize linefeeds and return.
  84. return grunt.util.normalizelf(tmpl);
  85. };