This script provides function T9r, which has some methods to detect, parse and replace tokens "{{ some_token }}" in a string with properties on an object.
My use case, to have "composable" json objects or strings, (used for configs), that can be populated with real values at runtime.
It's been a while since i worked in JS and i just wanted to get a gauge on my style, and general readability of my code, as well as see if there are any improvements i could make to the script.
Example Usage:
var str = '{{ App_Root }}/apple/sauce/{{ OK }}';
var context = { app_root: ':-)', Ok: '' };
T9r.replaceTokens(str,context);
Module:
// T9r ~ "T{oken Parse}(9)r"
const T9r = function(){};
/**
A helper function to process and replace
all tokens within a string.
*/
T9r.replaceTokens = function(Str, Context){
return T9r.parseTokens( T9r.extractTokens(Str), Context, Str );
};
/**
Will match all tokens "{{ some_token }}, {{some_token}},
{{ Some_ToKen }}" within a body of text, extracting all
matches ( Tokens ), while pruning each match, removing
the opening and closing curly brakets, as well as strip out any
whitespace, so we have text that can be used to lookup props
on an object.
Will return an empty array, if no tokens are found.
*/
T9r.extractTokens = function( str, pattern){
pattern = pattern ? pattern : /\{([^}]+)\}/ig;
var matches = str.match(pattern);
if( ! matches ) return [];
return T9r.pruneTokens(matches);
};
/**
Returns the count of Tokens that exist within a
string body.
*/
T9r.tokenCount = function( str, pattern){
pattern = pattern ? pattern : /\{([^}]+)\}/ig;
return str.match(pattern).length;
};
/**
Removes the leading and trailing wrapping-chars from
a token match, as well as strip out all whitespace.
*/
T9r.pruneTokens = function(Tokens){
Tokens.forEach(function( token, idx, tokens ){
tokens[idx] = token.slice(2,-1).replace(/\s+/g,'');
});
return Tokens;
};
/**
Checks to see if some reasonable version of a token exists,
within our context and returns the actual match. Otherwise returns
null.
*/
T9r.recognizedIn = function(token, context){
if( context[token] ) return token;
if( context[token.toLowerCase()] ) return token.toLowerCase();
if( context[token.toUpperCase()] ) return token.toUpperCase();
// Last ditch effort to find matches
for ( prop in context ) if(token.toLowerCase() === prop.toLowerCase()) return prop;
return null;
};
/**
Will loop through a set of tokens, replacing all matches within a
string body, with the values supplied via a context.
*/
T9r.parseTokens = function(Tokens , Context, Str ){
Tokens.forEach( function(token, idx, Tokens){
var TOKEN = T9r.recognizedIn(token, Context);
if( TOKEN !== null )
Str = T9r.parseToken(TOKEN, Context[TOKEN], Str);
});
return Str;
};
/**
Will automaticly escape Character Classes, for use by the RegExp
Contructor. For when composing RegExps dynamicly the symantics
of (\s) and other character classes can become very messy.
"/\s/"+some_var+"/\s/" must be written as "/\\s/"+some_var+"/\\s/"
You end up having to perform extra escaping, not for your pattern but
for the RegExp constructor, this becomes very messy, is
easy to forget and a little tricky to debug.
*/
T9r.escapeCharacterClasses = function(string){
return string.replace(new RegExp(/[\\s|\\S|\\w|\\W|\\d|\\D]/, "g"), '\\s');
}
/**
Wraps the value of a variable into a Regex that selects the whole
token, including the curly brackets.
*/
T9r.makeTokenPattern = function(variable, left, right, flags ){
left = left ? left : "\{{2}";
right = right ? right : "\}{2}";
flags = flags ? flags : "ig";
return new RegExp(T9r.escapeCharacterClasses(
left + ".?\s*(" + variable + ").\s*?" + right),
flags
);
}
/**
Within a string body, does the actual replacement of all instances of a token, with a
supplied value.
*/
T9r.parseToken = function(Token, Value, Str){
return Str.replace(T9r.makeTokenPattern(Token), Value);
};
module.exports.T9r = T9r;