index.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. 'use strict';
  2. var typeOf = require('kind-of');
  3. var extend = require('extend-shallow');
  4. /**
  5. * Parse sections in `input` with the given `options`.
  6. *
  7. * ```js
  8. * var sections = require('{%= name %}');
  9. * var result = sections(input, options);
  10. * // { content: 'Content before sections', sections: [] }
  11. * ```
  12. * @param {String|Buffer|Object} `input` If input is an object, it's `content` property must be a string or buffer.
  13. * @param {Object} options
  14. * @return {Object} Returns an object with a `content` string and an array of `sections` objects.
  15. * @api public
  16. */
  17. module.exports = function(input, options) {
  18. if (typeof options === 'function') {
  19. options = { parse: options };
  20. }
  21. var file = toObject(input);
  22. var defaults = {section_delimiter: '---', parse: identity};
  23. var opts = extend({}, defaults, options);
  24. var delim = opts.section_delimiter;
  25. var lines = file.content.split(/\r?\n/);
  26. var sections = null;
  27. var section = createSection();
  28. var content = [];
  29. var stack = [];
  30. function initSections(val) {
  31. file.content = val;
  32. sections = [];
  33. content = [];
  34. }
  35. function closeSection(val) {
  36. if (stack.length) {
  37. section.key = getKey(stack[0], delim);
  38. section.content = val;
  39. opts.parse(section, sections);
  40. sections.push(section);
  41. section = createSection();
  42. content = [];
  43. stack = [];
  44. }
  45. }
  46. for (var i = 0; i < lines.length; i++) {
  47. var line = lines[i];
  48. var len = stack.length;
  49. var ln = line.trim();
  50. if (isDelimiter(ln, delim)) {
  51. if (ln.length === 3 && i !== 0) {
  52. if (len === 0 || len === 2) {
  53. content.push(line);
  54. continue;
  55. }
  56. stack.push(ln);
  57. section.data = content.join('\n');
  58. content = [];
  59. continue;
  60. }
  61. if (sections === null) {
  62. initSections(content.join('\n'));
  63. }
  64. if (len === 2) {
  65. closeSection(content.join('\n'));
  66. }
  67. stack.push(ln);
  68. continue;
  69. }
  70. content.push(line);
  71. }
  72. if (sections === null) {
  73. initSections(content.join('\n'));
  74. } else {
  75. closeSection(content.join('\n'));
  76. }
  77. file.sections = sections;
  78. return file;
  79. };
  80. function isDelimiter(line, delim) {
  81. if (line.slice(0, delim.length) !== delim) {
  82. return false;
  83. }
  84. if (line.charAt(delim.length + 1) === delim.slice(-1)) {
  85. return false;
  86. }
  87. return true;
  88. }
  89. function toObject(input) {
  90. if (typeOf(input) !== 'object') {
  91. input = { content: input };
  92. }
  93. if (typeof input.content !== 'string' && !isBuffer(input.content)) {
  94. throw new TypeError('expected a buffer or string');
  95. }
  96. input.content = input.content.toString();
  97. input.sections = [];
  98. return input;
  99. }
  100. function getKey(val, delim) {
  101. return val ? val.slice(delim.length).trim() : '';
  102. }
  103. function createSection() {
  104. return { key: '', data: '', content: '' };
  105. }
  106. function identity(val) {
  107. return val;
  108. }
  109. function isBuffer(val) {
  110. if (val && val.constructor && typeof val.constructor.isBuffer === 'function') {
  111. return val.constructor.isBuffer(val);
  112. }
  113. return false;
  114. }