front_matter.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. 'use strict';
  2. var yaml = require('js-yaml');
  3. var util = require('util');
  4. var isDate = util.isDate;
  5. var rPrefixSep = /^(-{3,}|;{3,})/;
  6. var rFrontMatter = /^(-{3,}|;{3,})\n([\s\S]+?)\n\1(?:$|\n([\s\S]*)$)/;
  7. var rFrontMatterNew = /^([\s\S]+?)\n(-{3,}|;{3,})(?:$|\n([\s\S]*)$)/;
  8. function split(str) {
  9. if (typeof str !== 'string') throw new TypeError('str is required!');
  10. if (rFrontMatter.test(str)) return splitOld(str);
  11. if (rFrontMatterNew.test(str)) return splitNew(str);
  12. return {content: str};
  13. }
  14. function splitOld(str) {
  15. var match = str.match(rFrontMatter);
  16. return {
  17. data: match[2],
  18. content: match[3] || '',
  19. separator: match[1],
  20. prefixSeparator: true
  21. };
  22. }
  23. function splitNew(str) {
  24. if (rPrefixSep.test(str)) return {content: str};
  25. var match = str.match(rFrontMatterNew);
  26. return {
  27. data: match[1],
  28. content: match[3] || '',
  29. separator: match[2],
  30. prefixSeparator: false
  31. };
  32. }
  33. function parse(str, options) {
  34. if (typeof str !== 'string') throw new TypeError('str is required!');
  35. var splitData = split(str);
  36. var raw = splitData.data;
  37. if (!raw) return {_content: str};
  38. var data;
  39. if (splitData.separator[0] === ';') {
  40. data = parseJSON(raw);
  41. } else {
  42. data = parseYAML(raw, options);
  43. }
  44. if (!data) return {_content: str};
  45. var keys = Object.keys(data);
  46. var key = '';
  47. var item;
  48. // Convert timezone
  49. for (var i = 0, len = keys.length; i < len; i++) {
  50. key = keys[i];
  51. item = data[key];
  52. if (isDate(item)) {
  53. data[key] = new Date(item.getTime() + item.getTimezoneOffset() * 60 * 1000);
  54. }
  55. }
  56. data._content = splitData.content;
  57. return data;
  58. }
  59. function parseYAML(str, options) {
  60. var result = yaml.load(escapeYAML(str), options);
  61. if (typeof result !== 'object') return;
  62. return result;
  63. }
  64. function parseJSON(str) {
  65. try {
  66. return JSON.parse('{' + str + '}');
  67. } catch (err) {
  68. return;
  69. }
  70. }
  71. function escapeYAML(str) {
  72. if (typeof str !== 'string') throw new TypeError('str is required!');
  73. return str.replace(/\n(\t+)/g, function(match, tabs) {
  74. var result = '\n';
  75. for (var i = 0, len = tabs.length; i < len; i++) {
  76. result += ' ';
  77. }
  78. return result;
  79. });
  80. }
  81. function stringify(obj, options) {
  82. if (!obj) throw new TypeError('obj is required!');
  83. options = options || {};
  84. var content = obj._content || '';
  85. delete obj._content;
  86. if (!Object.keys(obj).length) return content;
  87. var mode = options.mode;
  88. var separator = options.separator || (mode === 'json' ? ';;;' : '---');
  89. var result = '';
  90. if (options.prefixSeparator) result += separator + '\n';
  91. if (mode === 'json') {
  92. result += stringifyJSON(obj);
  93. } else {
  94. result += stringifyYAML(obj, options);
  95. }
  96. result += separator + '\n' + content;
  97. return result;
  98. }
  99. function stringifyYAML(obj, options) {
  100. var keys = Object.keys(obj);
  101. var data = {};
  102. var nullKeys = [];
  103. var dateKeys = [];
  104. var key, value, i, len;
  105. for (i = 0, len = keys.length; i < len; i++) {
  106. key = keys[i];
  107. value = obj[key];
  108. if (value == null) {
  109. nullKeys.push(key);
  110. } else if (isDate(value)) {
  111. dateKeys.push(key);
  112. } else {
  113. data[key] = value;
  114. }
  115. }
  116. var result = yaml.dump(data, options);
  117. if (dateKeys.length) {
  118. for (i = 0, len = dateKeys.length; i < len; i++) {
  119. key = dateKeys[i];
  120. result += key + ': ' + formatDate(obj[key]) + '\n';
  121. }
  122. }
  123. if (nullKeys.length) {
  124. for (i = 0, len = nullKeys.length; i < len; i++) {
  125. result += nullKeys[i] + ':\n';
  126. }
  127. }
  128. return result;
  129. }
  130. function stringifyJSON(obj) {
  131. return JSON.stringify(obj, null, ' ')
  132. // Remove indention
  133. .replace(/\n {2}/g, function() {
  134. return '\n';
  135. })
  136. // Remove prefixing and trailing braces
  137. .replace(/^{\n|}$/g, '');
  138. }
  139. function doubleDigit(num) {
  140. return num < 10 ? '0' + num : num;
  141. }
  142. function formatDate(date) {
  143. return date.getFullYear() + '-' +
  144. doubleDigit(date.getMonth() + 1) + '-' +
  145. doubleDigit(date.getDate()) + ' ' +
  146. doubleDigit(date.getHours()) + ':' +
  147. doubleDigit(date.getMinutes()) + ':' +
  148. doubleDigit(date.getSeconds());
  149. }
  150. exports = module.exports = parse;
  151. exports.parse = parse;
  152. exports.split = split;
  153. exports.escape = escapeYAML;
  154. exports.stringify = stringify;