Guest User

Untitled

a guest
Jan 17th, 2018
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.75 KB | None | 0 0
  1. const MATCH_TAGS = /{%.+?%}/g;
  2.  
  3. const MACH_TAG_NAME = /{%\s+?([a-zA-Z]+)/;
  4.  
  5. const MATCH_WITH = /with.+/;
  6.  
  7. const MATCH_ASSIGNMENTS = /(.+?)\s*=\s*('.*?')/g;
  8.  
  9. const MATCH_QUOTE = /'|"/g;
  10.  
  11. const MATCH_DOUBLE_SLASH = /\/\/+/g;
  12.  
  13.  
  14. class TemplateCompiler {
  15. constructor(template, context={}, requiredFiles={}, staticURL='/static/') {
  16. this.template = template;
  17.  
  18. this.context = context;
  19.  
  20. this.requiredFiles = requiredFiles;
  21.  
  22. this.staticURL = staticURL;
  23.  
  24. this.tags = this.extractTags();
  25.  
  26. this.includes = this.extractIncludes();
  27.  
  28. this.children = this.includeChildren();
  29.  
  30. this.loads = this.extractLoads();
  31.  
  32. this.statics = this.extractStatics();
  33. }
  34.  
  35. /**
  36. * Extracts the tags from the this.template.
  37. * @returns {string[]}
  38. */
  39. extractTags() {
  40. let tags = this.template.match(MATCH_TAGS);
  41.  
  42. if (tags && tags.length) {
  43. return tags.map(match => new Tag(match, this.context));
  44. }
  45. return [];
  46.  
  47. }
  48.  
  49. /**
  50. * Extracts the include tags from this.tags.
  51. * @returns {Tag[]}
  52. */
  53. extractIncludes() {
  54. return this.tags.filter(tag => tag.name === 'include');
  55. }
  56.  
  57. /**
  58. *
  59. * @returns {TemplateCompiler[]}
  60. */
  61. includeChildren() {
  62. return this.includes.map(this.includeChild.bind(this));
  63. }
  64.  
  65. /**
  66. *
  67. * @param tag
  68. * @returns {TemplateCompiler}
  69. */
  70. includeChild(tag) {
  71. let path = tag.args[0].replace(MATCH_QUOTE, '');
  72. let template = this.requiredFiles[path];
  73.  
  74. if (!template) {
  75. throw new Error(`Unable to find template for ${path} in ${this.requiredFiles}.`);
  76. }
  77.  
  78. let context = tag.context;
  79. return new TemplateCompiler(template, context, this.requiredFiles, this.staticURL);
  80. }
  81.  
  82. /**
  83. *
  84. * @param input
  85. * @returns {*}
  86. */
  87. replaceChildren(input=this.template) {
  88. return this.includes.reduce((acc, value, index) => {
  89. let target = value.templateTag;
  90. let replacement = this.children[index].toHTML();
  91. return acc.replace(target, replacement);
  92. }, input);
  93. }
  94.  
  95. /**
  96. *
  97. * @returns {Tag[]}
  98. */
  99. extractLoads() {
  100. return this.tags.filter(tag => tag.name === 'load');
  101. }
  102.  
  103. /**
  104. *
  105. * @param input
  106. * @returns {*}
  107. */
  108. replaceLoads(input=this.template) {
  109. return this.loads.reduce((acc, value) => {
  110. let target = value.templateTag;
  111. let replacement = '';
  112. return acc.replace(target, replacement);
  113. }, input);
  114. }
  115.  
  116. extractStatics() {
  117. return this.tags.filter(tag => tag.name === 'static');
  118. }
  119.  
  120. /**
  121. *
  122. * @param input
  123. * @returns {*}
  124. */
  125. replaceStatics(input=this.template) {
  126. return this.statics.reduce((acc, value) => {
  127. let target = value.templateTag;
  128. let path = value.args[0];
  129. let replacement = `${this.staticURL}/${path.replace(/'|"/g, '')}`.replace(MATCH_DOUBLE_SLASH, '/');
  130. return acc.replace(target, replacement);
  131. }, input);
  132. }
  133.  
  134. /**
  135. *
  136. * @param input
  137. * @returns {*}
  138. */
  139. replaceVars(input=this.template) {
  140. let keys = Object.keys(this.context);
  141.  
  142. keys.forEach(key => {
  143. let value = this.context[key];
  144. input = input.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), value)
  145. });
  146. return input;
  147. }
  148.  
  149. /**
  150. *
  151. * @returns {*}
  152. */
  153. toHTML() {
  154. let html = this.template;
  155. html = this.replaceChildren(html);
  156. html = this.replaceLoads(html);
  157. html = this.replaceStatics(html);
  158. html = this.replaceVars(html);
  159. return html;
  160. }
  161. }
  162.  
  163. class Tag {
  164. constructor(templateTag, templateContext={}) {
  165. this.templateTag = templateTag;
  166.  
  167. this.parentContext = templateContext;
  168.  
  169. this.context = this.extractContext();
  170.  
  171. this.name = this.extractName();
  172.  
  173. this.args = this.extractArgs();
  174. }
  175.  
  176. /**
  177. * Extracts the name from this.templateTag.
  178. * @returns {string}
  179. */
  180. extractName() {
  181. return this.templateTag.match(MACH_TAG_NAME)[1];
  182. }
  183.  
  184. /**
  185. * Extracts the arguments from this.templateTag.
  186. * The "with section" is omitted.
  187. * @returns {string[]}
  188. */
  189. extractArgs() {
  190. let noBrackets = this.templateTag.substr(2, this.templateTag.length -4).trim();
  191. let noWith = noBrackets.replace(MATCH_WITH, '');
  192. let parts = noWith.split(' ');
  193. return parts.splice(1);
  194. }
  195.  
  196. /**
  197. * Extract the context from the "with section" and combines them with this.parentContext.
  198. * Only single quoted strings are supported for now.
  199. */
  200. extractContext() {
  201. let context = JSON.parse(JSON.stringify(this.parentContext));
  202.  
  203. let additionalContext = this.templateTag.match(MATCH_WITH);
  204.  
  205. if (additionalContext && additionalContext.length) {
  206. let noWith = additionalContext[0].substr(4).trim();
  207. let noBrackets = noWith.substr(0, noWith.length - 2).trim();
  208. let assignments = noBrackets.match(MATCH_ASSIGNMENTS);
  209.  
  210. if (assignments) {
  211. assignments.forEach(assignment => {
  212. let [key, value] = assignment.split('=');
  213. context[key.trim()] = value.trim();
  214. });
  215. }
  216. }
  217.  
  218. return context;
  219.  
  220. }
  221. }
  222.  
  223.  
  224. let requiredFiles = {
  225. 'includes/button/button.html': buttonTemplate,
  226. 'includes/button/_content.html': contentTemplate,
  227. 'includes/form/checkbox.html': checkboxTemplate,
  228. 'includes/education/tags.html': tagsTemplate,
  229. 'includes/education/tag.html': tagTemplate
  230. };
  231.  
  232. console.log('Compiled:', new TemplateCompiler(advancedFilterTemplate , {}, requiredFiles, '/static/').toHTML());
Add Comment
Please, Sign In to add comment