Created
September 13, 2020 14:35
-
-
Save ivawzh/44cc105072b287d2793e19a1b77ac6e5 to your computer and use it in GitHub Desktop.
TypeScript utils
This file contains hidden or 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
//@ts-nocheck | |
// copied from https://github.com/zspecza/common-tags/blob/master/src/stripIndents/stripIndents.js | |
const tagTransformersSymbol = 'COMMON_TAGS_TAG_TRANSFORMERS_SYMBOL'; | |
function isTag(fn) { | |
return typeof fn === 'function' && fn[tagTransformersSymbol]; | |
} | |
function cleanTransformers(transformers) { | |
return flat(transformers).reduce( | |
(transformers, transformer) => | |
isTag(transformer) | |
? [...transformers, ...transformer[tagTransformersSymbol]] | |
: [...transformers, transformer], | |
[], | |
); | |
} | |
/** | |
* An intermediary template tag that receives a template tag and passes the result of calling the template with the received | |
* template tag to our own template tag. | |
* @param {Function} nextTag - The received template tag | |
* @param {Array<String>} template - The template to process | |
* @param {...*} ...substitutions - `substitutions` is an array of all substitutions in the template | |
* @return {*} - The final processed value | |
*/ | |
function getInterimTag(originalTag, extraTag) { | |
return function tag(...args) { | |
return originalTag(['', ''], extraTag(...args)); | |
}; | |
} | |
function getTagCallInfo(transformers) { | |
return { | |
transformers, | |
context: transformers.map((transformer) => | |
transformer.getInitialContext ? transformer.getInitialContext() : {}, | |
), | |
}; | |
} | |
/** | |
* Iterate through each transformer, calling the transformer's specified hook. | |
* @param {Array<Function>} transformers - The transformer functions | |
* @param {String} hookName - The name of the hook | |
* @param {String} initialString - The input string | |
* @return {String} - The final results of applying each transformer | |
*/ | |
function applyHook0({ transformers, context }, hookName, initialString) { | |
return transformers.reduce( | |
(result, transformer, index) => | |
transformer[hookName] | |
? transformer[hookName](result, context[index]) | |
: result, | |
initialString, | |
); | |
} | |
/** | |
* Iterate through each transformer, calling the transformer's specified hook. | |
* @param {Array<Function>} transformers - The transformer functions | |
* @param {String} hookName - The name of the hook | |
* @param {String} initialString - The input string | |
* @param {*} arg1 - An additional argument passed to the hook | |
* @return {String} - The final results of applying each transformer | |
*/ | |
function applyHook1({ transformers, context }, hookName, initialString, arg1) { | |
return transformers.reduce( | |
(result, transformer, index) => | |
transformer[hookName] | |
? transformer[hookName](result, arg1, context[index]) | |
: result, | |
initialString, | |
); | |
} | |
/** | |
* Consumes a pipeline of composable transformer plugins and produces a template tag. | |
* @param {...Object} [...rawTransformers] - An array or arguments list of transformers | |
* @return {Function} - A template tag | |
*/ | |
export default function createTag(...rawTransformers) { | |
const transformers = cleanTransformers(rawTransformers); | |
function tag(strings, ...expressions) { | |
if (typeof strings === 'function') { | |
// if the first argument passed is a function, assume it is a template tag and return | |
// an intermediary tag that processes the template using the aforementioned tag, passing the | |
// result to our tag | |
return getInterimTag(tag, strings); | |
} | |
if (!Array.isArray(strings)) { | |
return tag([strings]); | |
} | |
const tagCallInfo = getTagCallInfo(transformers); | |
// if the first argument is an array, return a transformed end result of processing the template with our tag | |
const processedTemplate = strings | |
.map((string) => applyHook0(tagCallInfo, 'onString', string)) | |
.reduce((result, string, index) => | |
''.concat( | |
result, | |
applyHook1( | |
tagCallInfo, | |
'onSubstitution', | |
expressions[index - 1], | |
result, | |
), | |
string, | |
), | |
); | |
return applyHook0(tagCallInfo, 'onEndResult', processedTemplate); | |
} | |
tag[tagTransformersSymbol] = transformers; | |
return tag; | |
} | |
function flat(array) { | |
return [].concat(...array); | |
} | |
const stripIndentTransformerSupportedTypes = ['initial', 'all']; | |
/** | |
* strips indentation from a template literal | |
* @param {String} type = 'initial' - whether to remove all indentation or just leading indentation. can be 'all' or 'initial' | |
* @return {Object} - a TemplateTag transformer | |
*/ | |
const stripIndentTransformer = (type = 'initial') => { | |
if (!stripIndentTransformerSupportedTypes.includes(type)) { | |
throw new Error(`Type not supported: ${type}`); | |
} | |
return { | |
onEndResult(endResult) { | |
if (type === 'all') { | |
// remove all indentation from each line | |
return endResult.replace(/^[^\S\n]+/gm, ''); | |
} | |
// remove the shortest leading indentation from each line | |
const match = endResult.match(/^[^\S\n]*(?=\S)/gm); | |
const indent = match && Math.min(...match.map((el) => el.length)); | |
if (indent) { | |
const regexp = new RegExp(`^.{${indent}}`, 'gm'); | |
return endResult.replace(regexp, ''); | |
} | |
return endResult; | |
}, | |
}; | |
}; | |
const trimResultTransformerSupportedSides = ['', 'start', 'left', 'end', 'right', 'smart']; | |
/** | |
* TemplateTag transformer that trims whitespace on the end result of a tagged template | |
* @param {String} side = '' - The side of the string to trim. Can be 'start' or 'end' (alternatively 'left' or 'right') | |
* @return {Object} - a TemplateTag transformer | |
*/ | |
const trimResultTransformer = (side = '') => { | |
if (!trimResultTransformerSupportedSides.includes(side)) { | |
throw new Error(`Side not supported: ${side}`); | |
} | |
return { | |
onEndResult(endResult) { | |
switch (side) { | |
case '': | |
return endResult.trim(); | |
case 'start': | |
case 'left': | |
return endResult.replace(/^\s*/, ''); | |
case 'end': | |
case 'right': | |
return endResult.replace(/\s*$/, ''); | |
case 'smart': | |
return endResult.replace(/[^\S\n]+$/gm, '').replace(/^\n/, ''); | |
} | |
}, | |
}; | |
}; | |
export const stripIndents = createTag( | |
stripIndentTransformer('all'), | |
trimResultTransformer('smart'), | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment