compile.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. var template = require("./index")
  2. var whitespaceRegex = /["'\\\n\r\u2028\u2029]/g
  3. var nargs = /\{[0-9a-zA-Z]+\}/g
  4. var replaceTemplate =
  5. " var args\n" +
  6. " var result\n" +
  7. " if (arguments.length === 1 && typeof arguments[0] === \"object\") {\n" +
  8. " args = arguments[0]\n" +
  9. " } else {\n" +
  10. " args = arguments" +
  11. " }\n\n" +
  12. " if (!args || !(\"hasOwnProperty\" in args)) {\n" +
  13. " args = {}\n" +
  14. " }\n\n" +
  15. " return {0}"
  16. var literalTemplate = "\"{0}\""
  17. var argTemplate = "(result = args.hasOwnProperty(\"{0}\") ? " +
  18. "args[\"{0}\"] : null, \n " +
  19. "(result === null || result === undefined) ? \"\" : result)"
  20. module.exports = compile
  21. function compile(string, inline) {
  22. var replacements = string.match(nargs)
  23. var interleave = string.split(nargs)
  24. var replace = []
  25. for (var i = 0; i < interleave.length; i++) {
  26. var current = interleave[i];
  27. var replacement = replacements[i];
  28. var escapeLeft = current.charAt(current.length - 1)
  29. var escapeRight = (interleave[i + 1] || "").charAt(0)
  30. if (replacement) {
  31. replacement = replacement.substring(1, replacement.length - 1)
  32. }
  33. if (escapeLeft === "{" && escapeRight === "}") {
  34. replace.push(current + replacement)
  35. } else {
  36. replace.push(current);
  37. if (replacement) {
  38. replace.push({ name: replacement })
  39. }
  40. }
  41. }
  42. var prev = [""]
  43. for (var j = 0; j < replace.length; j++) {
  44. var curr = replace[j]
  45. if (String(curr) === curr) {
  46. var top = prev[prev.length - 1]
  47. if (String(top) === top) {
  48. prev[prev.length - 1] = top + curr
  49. } else {
  50. prev.push(curr)
  51. }
  52. } else {
  53. prev.push(curr)
  54. }
  55. }
  56. replace = prev
  57. if (inline) {
  58. for (var k = 0; k < replace.length; k++) {
  59. var token = replace[k]
  60. if (String(token) === token) {
  61. replace[k] = template(literalTemplate, escape(token))
  62. } else {
  63. replace[k] = template(argTemplate, escape(token.name))
  64. }
  65. }
  66. var replaceCode = replace.join(" +\n ")
  67. var compiledSource = template(replaceTemplate, replaceCode)
  68. return new Function(compiledSource)
  69. }
  70. return function template() {
  71. var args
  72. if (arguments.length === 1 && typeof arguments[0] === "object") {
  73. args = arguments[0]
  74. } else {
  75. args = arguments
  76. }
  77. if (!args || !("hasOwnProperty" in args)) {
  78. args = {}
  79. }
  80. var result = []
  81. for (var i = 0; i < replace.length; i++) {
  82. if (i % 2 === 0) {
  83. result.push(replace[i])
  84. } else {
  85. var argName = replace[i].name
  86. var arg = args.hasOwnProperty(argName) ? args[argName] : null
  87. if (arg !== null || arg !== undefined) {
  88. result.push(arg)
  89. }
  90. }
  91. }
  92. return result.join("")
  93. }
  94. }
  95. function escape(string) {
  96. string = '' + string;
  97. return string.replace(whitespaceRegex, escapedWhitespace);
  98. }
  99. function escapedWhitespace(character) {
  100. // Escape all characters not included in SingleStringCharacters and
  101. // DoubleStringCharacters on
  102. // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
  103. switch (character) {
  104. case '"':
  105. case "'":
  106. case '\\':
  107. return '\\' + character
  108. // Four possible LineTerminator characters need to be escaped:
  109. case '\n':
  110. return '\\n'
  111. case '\r':
  112. return '\\r'
  113. case '\u2028':
  114. return '\\u2028'
  115. case '\u2029':
  116. return '\\u2029'
  117. }
  118. }