Last active
February 12, 2024 00:33
-
-
Save unscriptable/f50c7738b9985300a0320d31c203d039 to your computer and use it in GitHub Desktop.
Mostly for fun, I created a simple template function using functional JavaScript patterns and ES6 syntax. Kinda like mustache, but much simpler.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Take a text string containing tokens (of type `${name}`) and return | |
// a function that will replace the tokens with the properties of a given | |
// object. If we need to get much more sophisticated, we should | |
// probably use mustache or similar. | |
// TODO: allow dev to specify a format for each token? | |
export default | |
template => createRenderAll(partition(String(template))) | |
// ------------------------------------------------------------ | |
// Given a set of compiled template render functions, return a function that | |
// runs the functions on the input data and merges the resulting text. | |
// Note: this is the function that is the most important to performance, but | |
// I'm leaving it in this ultra-simple form unless there's a requirement | |
// for more speed. | |
const createRenderAll | |
= parts => props => parts.map(createRenderEach(props)).join('') | |
// Given an object of properties, return a function to convert a template part | |
// (text run or token) to text. | |
const createRenderEach | |
= props => render => render(props) | |
// Find template parts (text runs and tokens) in a template. | |
const partition | |
= template => nextToken(findToken(findTokensRx()), template) | |
// Recursively find the next token in a text string. | |
const nextToken | |
= (findToken, text, start=0, parts=[]) => { | |
const [ name, pos, next ] = findToken(text) | |
return name | |
? nextToken( | |
findToken, text, next, | |
parts.concat([textRender(text, start, pos), tokenRender(name)]) | |
) | |
: parts.concat(textRender(text, start)) | |
} | |
// Given a regular expression, return a function that accepts a text string | |
// and returns a triple of token information: | |
// [name-of-token, position-of-token, position-to-start-next-search]. | |
// If no token is found, an "empty" triple is returned. | |
const findToken | |
= rx => text => { | |
const r = rx.exec(text) | |
return r ? [ r[1], r.index, rx.lastIndex ] : [] | |
} | |
// RegExp to find simple `${name}` tokens. | |
// We create a new RegExp each time as a "good habit". If this code were | |
// async and we reused the same RegExp object for multiple templates, | |
// we'd suffer from hard-to-find *mutable state* bugs! | |
const findTokensRx | |
= () => /\$\{([^}]*)\}/g | |
// Create a text render function from a slice of a template. | |
const textRender | |
= (text, start, end) => { | |
const snip = text.slice(start, end) | |
return () => snip | |
} | |
// Create a token render function from a token in a template. | |
const tokenRender | |
= token => { | |
const prop = token.trim() | |
return props => props && props[prop] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment