Skip to content

Commit

Permalink
add support for custom delimiters (fixes #29)
Browse files Browse the repository at this point in the history
  • Loading branch information
kbrsh committed Apr 12, 2017
1 parent 3cf8f66 commit c82ac63
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 43 deletions.
83 changes: 62 additions & 21 deletions dist/moon.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@
}
};

/**
* Escapes String Values for a Regular Expression
* @param {str} str
*/
var escapeRegex = function (str) {
return str.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
};

/**
* Does No Operation
*/
Expand Down Expand Up @@ -801,16 +809,26 @@
};

/* ======= Compiler ======= */
var openRE = /\{\{/;
var closeRE = /\s*\}\}/;
var modifierRE = /\[|\.|\(/;
var whitespaceRE = /\s/;

var compileTemplate = function (template, isString) {
/**
* Compiles a Template
* @param {String} template
* @param {Array} delimiters
* @param {Array} escapedDelimiters
* @param {Boolean} isString
* @return {String} compiled template
*/
var compileTemplate = function (template, delimiters, escapedDelimiters, isString) {
var state = {
current: 0,
template: template,
output: ""
output: "",
openDelimiterLen: delimiters[0].length,
closeDelimiterLen: delimiters[1].length,
openRE: new RegExp(escapedDelimiters[0]),
closeRE: new RegExp('\\s*' + escapedDelimiters[1])
};

compileTemplateState(state, isString);
Expand All @@ -823,7 +841,7 @@
var length = template.length;
while (state.current < length) {
// Match Text Between Templates
var value = scanTemplateStateUntil(state, openRE);
var value = scanTemplateStateUntil(state, state.openRE);

if (value) {
state.output += value;
Expand All @@ -834,14 +852,14 @@
break;
}

// Exit The Opening Tag
state.current += 2;
// Exit Opening Delimiter
state.current += state.openDelimiterLen;

// Consume whitespace
scanTemplateStateForWhitespace(state);

// Get the name of the opening tag
var name = scanTemplateStateUntil(state, closeRE);
var name = scanTemplateStateUntil(state, state.closeRE);

// If we've reached the end, the tag was unclosed
if (state.current === length) {
Expand Down Expand Up @@ -871,8 +889,8 @@
// Consume whitespace
scanTemplateStateForWhitespace(state);

// Exit mustache
state.current += 2;
// Exit closing delimiter
state.current += state.closeDelimiterLen;
}
};

Expand Down Expand Up @@ -1241,6 +1259,16 @@
return;
};

/**
* Delimiters (updated every time generation is called)
*/
var delimiters = null;

/**
* Escaped Delimiters
*/
var escapedDelimiters = null;

/**
* Generates Code for Props
* @param {Object} vnode
Expand Down Expand Up @@ -1303,7 +1331,7 @@
vnode.meta.shouldRender = true;
} else {
var normalizedProp = JSON.stringify(attrInfo.value);
var compiledProp = compileTemplate(normalizedProp, true);
var compiledProp = compileTemplate(normalizedProp, delimiters, escapedDelimiters, true);
if (normalizedProp !== compiledProp) {
vnode.meta.shouldRender = true;
}
Expand Down Expand Up @@ -1476,7 +1504,7 @@
if (typeof vnode === "string") {
// Escape newlines and double quotes, and compile the string
var escapedString = escapeString(vnode);
var compiledText = compileTemplate(escapedString, true);
var compiledText = compileTemplate(escapedString, delimiters, escapedDelimiters, true);
var textMeta = defaultMetadata();

if (escapedString !== compiledText) {
Expand Down Expand Up @@ -1531,6 +1559,18 @@
var generate = function (ast) {
// Get root element
var root = ast.children[0];

// Update delimiters if needed
var newDelimeters = null;
if ((newDelimeters = Moon.config.delimiters) !== delimiters) {
delimiters = newDelimeters;

// Escape delimiters
escapedDelimiters = new Array(2);
escapedDelimiters[0] = escapeRegex(delimiters[0]);
escapedDelimiters[1] = escapeRegex(delimiters[1]);
}

// Begin Code
var code = "var instance = this; return " + generateEl(root);

Expand Down Expand Up @@ -1896,6 +1936,7 @@
Moon.config = {
silent: "development" === "production" || typeof console === 'undefined',
prefix: "m-",
delimiters: ["{{", "}}"],
keyCodes: function (keyCodes) {
for (var keyCode in keyCodes) {
eventModifiersCode[keyCode] = 'if(event.keyCode !== ' + keyCodes[keyCode] + ') {return;};';
Expand Down Expand Up @@ -1999,15 +2040,15 @@

specialDirectives[Moon.config.prefix + "if"] = {
afterGenerate: function (value, meta, code, vnode) {
return '(' + compileTemplate(value, false) + ') ? ' + code + ' : null';
return '(' + compileTemplate(value, delimiters, escapedDelimiters, false) + ') ? ' + code + ' : null';
}
};

specialDirectives[Moon.config.prefix + "show"] = {
beforeGenerate: function (value, meta, vnode, parentVNode) {
var runTimeShowDirective = {
name: Moon.config.prefix + "show",
value: compileTemplate(value, false),
value: compileTemplate(value, delimiters, escapedDelimiters, false),
literal: true
};

Expand All @@ -2026,7 +2067,7 @@
// Aliases
var aliases = parts[0].split(",");
// The Iteratable
var iteratable = compileTemplate(parts[1], false);
var iteratable = compileTemplate(parts[1], delimiters, escapedDelimiters, false);

// Get any parameters
var params = aliases.join(",");
Expand All @@ -2048,7 +2089,7 @@
var rawModifiers = meta.arg.split(".");
var eventToCall = rawModifiers[0];
var params = "event";
var methodToCall = compileTemplate(value, false);
var methodToCall = compileTemplate(value, delimiters, escapedDelimiters, false);
var rawParams = methodToCall.split("(");

if (rawParams.length > 1) {
Expand All @@ -2075,7 +2116,7 @@
specialDirectives[Moon.config.prefix + "model"] = {
beforeGenerate: function (value, meta, vnode) {
// Compile a string value for the keypath
var compiledStringValue = compileTemplate(value, true);
var compiledStringValue = compileTemplate(value, delimiters, escapedDelimiters, true);
// Setup default event types and dom property to change
var eventType = "input";
var valueProp = "value";
Expand All @@ -2097,7 +2138,7 @@
}

// Setup a query used to get the value, and set the corresponding dom property
var getQuery = compileTemplate('{{' + compileTemplate(value, false) + '}}', false);
var getQuery = compileTemplate('{{' + compileTemplate(value, delimiters, escapedDelimiters, false) + '}}', delimiters, escapedDelimiters, false);
if (vnode.props.dom === undefined) {
vnode.props.dom = {};
}
Expand All @@ -2111,9 +2152,9 @@

if (prop === "class") {
// Classes need to be rendered differently
return '"class": instance.renderClass(' + compileTemplate(value, false) + '), ';
return '"class": instance.renderClass(' + compileTemplate(value, delimiters, escapedDelimiters, false) + '), ';
}
return '"' + prop + '": ' + compileTemplate(value, false) + ', ';
return '"' + prop + '": ' + compileTemplate(value, delimiters, escapedDelimiters, false) + ', ';
}
};

Expand All @@ -2122,7 +2163,7 @@
if (vnode.props.dom === undefined) {
vnode.props.dom = {};
}
vnode.props.dom.innerHTML = '"' + compileTemplate(value, true) + '"';
vnode.props.dom.innerHTML = '"' + compileTemplate(value, delimiters, escapedDelimiters, true) + '"';
}
};

Expand Down
2 changes: 1 addition & 1 deletion dist/moon.min.js

Large diffs are not rendered by default.

26 changes: 24 additions & 2 deletions src/compiler/generator.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* Delimiters (updated every time generation is called)
*/
let delimiters = null;

/**
* Escaped Delimiters
*/
let escapedDelimiters = null;

/**
* Generates Code for Props
* @param {Object} vnode
Expand Down Expand Up @@ -60,7 +70,7 @@ const generateProps = function(vnode, parentVNode) {
vnode.meta.shouldRender = true;
} else {
const normalizedProp = JSON.stringify(attrInfo.value);
const compiledProp = compileTemplate(normalizedProp, true);
const compiledProp = compileTemplate(normalizedProp, delimiters, escapedDelimiters, true);
if(normalizedProp !== compiledProp) {
vnode.meta.shouldRender = true;
}
Expand Down Expand Up @@ -233,7 +243,7 @@ const generateEl = function(vnode, parentVNode) {
if(typeof vnode === "string") {
// Escape newlines and double quotes, and compile the string
const escapedString = escapeString(vnode);
const compiledText = compileTemplate(escapedString, true);
const compiledText = compileTemplate(escapedString, delimiters, escapedDelimiters, true);
let textMeta = defaultMetadata();

if(escapedString !== compiledText) {
Expand Down Expand Up @@ -288,6 +298,18 @@ const generateEl = function(vnode, parentVNode) {
const generate = function(ast) {
// Get root element
const root = ast.children[0];

// Update delimiters if needed
let newDelimeters = null;
if((newDelimeters = Moon.config.delimiters) !== delimiters) {
delimiters = newDelimeters;

// Escape delimiters
escapedDelimiters = new Array(2);
escapedDelimiters[0] = escapeRegex(delimiters[0]);
escapedDelimiters[1] = escapeRegex(delimiters[1]);
}

// Begin Code
const code = "var instance = this; return " + generateEl(root);

Expand Down
30 changes: 20 additions & 10 deletions src/compiler/template.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
const openRE = /\{\{/;
const closeRE = /\s*\}\}/;
const modifierRE = /\[|\.|\(/;
const whitespaceRE = /\s/;

const compileTemplate = function(template, isString) {
/**
* Compiles a Template
* @param {String} template
* @param {Array} delimiters
* @param {Array} escapedDelimiters
* @param {Boolean} isString
* @return {String} compiled template
*/
const compileTemplate = function(template, delimiters, escapedDelimiters, isString) {
let state = {
current: 0,
template: template,
output: ""
output: "",
openDelimiterLen: delimiters[0].length,
closeDelimiterLen: delimiters[1].length,
openRE: new RegExp(escapedDelimiters[0]),
closeRE: new RegExp(`\\s*${escapedDelimiters[1]}`)
};

compileTemplateState(state, isString);
Expand All @@ -20,7 +30,7 @@ const compileTemplateState = function(state, isString) {
const length = template.length;
while(state.current < length) {
// Match Text Between Templates
const value = scanTemplateStateUntil(state, openRE);
const value = scanTemplateStateUntil(state, state.openRE);

if(value) {
state.output += value;
Expand All @@ -31,14 +41,14 @@ const compileTemplateState = function(state, isString) {
break;
}

// Exit The Opening Tag
state.current += 2;
// Exit Opening Delimiter
state.current += state.openDelimiterLen;

// Consume whitespace
scanTemplateStateForWhitespace(state);

// Get the name of the opening tag
let name = scanTemplateStateUntil(state, closeRE);
let name = scanTemplateStateUntil(state, state.closeRE);

// If we've reached the end, the tag was unclosed
if(state.current === length) {
Expand Down Expand Up @@ -68,8 +78,8 @@ const compileTemplateState = function(state, isString) {
// Consume whitespace
scanTemplateStateForWhitespace(state);

// Exit mustache
state.current += 2;
// Exit closing delimiter
state.current += state.closeDelimiterLen;
}
}

Expand Down
Loading

0 comments on commit c82ac63

Please sign in to comment.