Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const MATCH_TAGS = /{%.+?%}/g;
- const MACH_TAG_NAME = /{%\s+?([a-zA-Z]+)/;
- const MATCH_WITH = /with.+/;
- const MATCH_ASSIGNMENTS = /(.+?)\s*=\s*('.*?')/g;
- const MATCH_QUOTE = /'|"/g;
- const MATCH_DOUBLE_SLASH = /\/\/+/g;
- class TemplateCompiler {
- constructor(template, context={}, requiredFiles={}, staticURL='/static/') {
- this.template = template;
- this.context = context;
- this.requiredFiles = requiredFiles;
- this.staticURL = staticURL;
- this.tags = this.extractTags();
- this.includes = this.extractIncludes();
- this.children = this.includeChildren();
- this.loads = this.extractLoads();
- this.statics = this.extractStatics();
- }
- /**
- * Extracts the tags from the this.template.
- * @returns {string[]}
- */
- extractTags() {
- let tags = this.template.match(MATCH_TAGS);
- if (tags && tags.length) {
- return tags.map(match => new Tag(match, this.context));
- }
- return [];
- }
- /**
- * Extracts the include tags from this.tags.
- * @returns {Tag[]}
- */
- extractIncludes() {
- return this.tags.filter(tag => tag.name === 'include');
- }
- /**
- *
- * @returns {TemplateCompiler[]}
- */
- includeChildren() {
- return this.includes.map(this.includeChild.bind(this));
- }
- /**
- *
- * @param tag
- * @returns {TemplateCompiler}
- */
- includeChild(tag) {
- let path = tag.args[0].replace(MATCH_QUOTE, '');
- let template = this.requiredFiles[path];
- if (!template) {
- throw new Error(`Unable to find template for ${path} in ${this.requiredFiles}.`);
- }
- let context = tag.context;
- return new TemplateCompiler(template, context, this.requiredFiles, this.staticURL);
- }
- /**
- *
- * @param input
- * @returns {*}
- */
- replaceChildren(input=this.template) {
- return this.includes.reduce((acc, value, index) => {
- let target = value.templateTag;
- let replacement = this.children[index].toHTML();
- return acc.replace(target, replacement);
- }, input);
- }
- /**
- *
- * @returns {Tag[]}
- */
- extractLoads() {
- return this.tags.filter(tag => tag.name === 'load');
- }
- /**
- *
- * @param input
- * @returns {*}
- */
- replaceLoads(input=this.template) {
- return this.loads.reduce((acc, value) => {
- let target = value.templateTag;
- let replacement = '';
- return acc.replace(target, replacement);
- }, input);
- }
- extractStatics() {
- return this.tags.filter(tag => tag.name === 'static');
- }
- /**
- *
- * @param input
- * @returns {*}
- */
- replaceStatics(input=this.template) {
- return this.statics.reduce((acc, value) => {
- let target = value.templateTag;
- let path = value.args[0];
- let replacement = `${this.staticURL}/${path.replace(/'|"/g, '')}`.replace(MATCH_DOUBLE_SLASH, '/');
- return acc.replace(target, replacement);
- }, input);
- }
- /**
- *
- * @param input
- * @returns {*}
- */
- replaceVars(input=this.template) {
- let keys = Object.keys(this.context);
- keys.forEach(key => {
- let value = this.context[key];
- input = input.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), value)
- });
- return input;
- }
- /**
- *
- * @returns {*}
- */
- toHTML() {
- let html = this.template;
- html = this.replaceChildren(html);
- html = this.replaceLoads(html);
- html = this.replaceStatics(html);
- html = this.replaceVars(html);
- return html;
- }
- }
- class Tag {
- constructor(templateTag, templateContext={}) {
- this.templateTag = templateTag;
- this.parentContext = templateContext;
- this.context = this.extractContext();
- this.name = this.extractName();
- this.args = this.extractArgs();
- }
- /**
- * Extracts the name from this.templateTag.
- * @returns {string}
- */
- extractName() {
- return this.templateTag.match(MACH_TAG_NAME)[1];
- }
- /**
- * Extracts the arguments from this.templateTag.
- * The "with section" is omitted.
- * @returns {string[]}
- */
- extractArgs() {
- let noBrackets = this.templateTag.substr(2, this.templateTag.length -4).trim();
- let noWith = noBrackets.replace(MATCH_WITH, '');
- let parts = noWith.split(' ');
- return parts.splice(1);
- }
- /**
- * Extract the context from the "with section" and combines them with this.parentContext.
- * Only single quoted strings are supported for now.
- */
- extractContext() {
- let context = JSON.parse(JSON.stringify(this.parentContext));
- let additionalContext = this.templateTag.match(MATCH_WITH);
- if (additionalContext && additionalContext.length) {
- let noWith = additionalContext[0].substr(4).trim();
- let noBrackets = noWith.substr(0, noWith.length - 2).trim();
- let assignments = noBrackets.match(MATCH_ASSIGNMENTS);
- if (assignments) {
- assignments.forEach(assignment => {
- let [key, value] = assignment.split('=');
- context[key.trim()] = value.trim();
- });
- }
- }
- return context;
- }
- }
- let requiredFiles = {
- 'includes/button/button.html': buttonTemplate,
- 'includes/button/_content.html': contentTemplate,
- 'includes/form/checkbox.html': checkboxTemplate,
- 'includes/education/tags.html': tagsTemplate,
- 'includes/education/tag.html': tagTemplate
- };
- console.log('Compiled:', new TemplateCompiler(advancedFilterTemplate , {}, requiredFiles, '/static/').toHTML());
Add Comment
Please, Sign In to add comment