Subschema expression allows for expressions to be used within Subschema. It used by Content and the expression resolver to take a string look up the substitutable values in the value manager and return a string.
It is meant to trust the formatting string, but not the input (i.e. value manager); So to prevent script injection all evaluated output is escaped. However the string format is not. So you can output html in the format but not in the values.
$ npm install subschema-expression
The import takes a string and parses to an expression object that looks like { format(dataObject, formatterObject), listen:['array of substituted values'], formatters:['array of formatter names'] }
The format function on the expression object can be called multiple times. The listen array, has a list of variables to be substituted with the correspoinding keys in the dataObject
import expression from 'subschema-expression';
const exprObj = expression('hello {world}');
const str = exprObj.format({
world: 'Joe'
});
//str is 'hello Joe';
You can also use expression strings with functions. If the argument to the function is quoted '' or "" it is passed as a literal. If it is not quoted it is resolved against the object that is passed to format.
import expression from 'subschema-expression';
const exprObj = expression('hello {comma(you, "me")} and {uppercase(world)}');
const str = exprObj.format({
world: 'Joe',
you: 'Bob'
}, {
uppercase(f){
return f == null ? '' : f.toUpperCase()
},
comma(...args){
return args.join(', ');
}
});
//str is now 'hello Bob, me and JOE'
You can use nested paths following the convention used by lodash get. Basically using '.' to descend objects.
import expression from 'subschema-expression';
const exprObj = expression('hello {uppercase(name.first)} and {name.last}');
const str = exprObj.format({
name: {
first: 'Joe',
last: 'Bob'
}
}, {
uppercase(f){
return f == null ? '' : f.toUpperCase()
}
});
//str is hello JOE and Bob
An expression once compiled is reusable.
import expression from 'subschema-expression';
const exprObj = expression('hello {uppercase(name.first)} and {name.last}');
const formatters = {
uppercase(f){
return f == null ? '' : f.toUpperCase()
}
};
let str = exprObj.format({
name: {
first: 'Joe',
last: 'Bob'
}
}, formatters);
//str is hello JOE and Bob
str = exprObj.format({
name: {
first: 'Billy',
last: 'Joe'
}
}, formatters);
//str is hello BILLY and Joe
You may want a formatter to return html. Well I can't stop you, but if you do, you can return a string wrapped with '--' and it will not escape its output. Its up to you to sanitize the input in this case. Please use caution.
import expression from 'subschema-expression';
import loscape from 'lodash/string/escape';
const exprObj = expression('hello {h1(name.first)}');
const formatters = {
h1(f){
return `--<h1>${f == null ? '' : loscape(f.toUpperCase())}</h1>--`;
}
};
let str = exprObj.format({
name: {
first: 'Joe<B/>',
last: 'Bob'
}
}, formatters);
//str is hello<h1>JOE<B/></h1>
So sometimes its nice to run karma for debugging. To do so you will need to change test/*-test.js from
var expression = require('../dist/expression');
to
var expression = require('subschema-expression');
$ npm run karma