Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | 27x 159x 158x 80x 78x 27x 27x 27x 1529x 85x 1444x 67x 1377x 27x 100x 54x 46x 46x 44x 44x 2x 27x 54x 54x 27x 44x 44x 27x 111x 111x 27x 2x 1x 1x 2x | /** * Invoke an EvalFunc in the specified EvalContext. * * @param evalFunction Function to invoke * @param evalContext Context to execute within * @returns The return value of evalFunction */ import {EvalContext, EvalFunction} from '../..'; export async function invokeEvalFunc<T>(evalFunction: EvalFunction<T>, evalContext: EvalContext): Promise<T> { // execute the function const returnOrPromise = evalFunction.call(evalContext.scope, evalContext.scope, evalContext, requireFromRoot); if (returnOrPromise instanceof Promise) { // If its a promise, await it return await returnOrPromise; } else { // If not a promise, then return directly return returnOrPromise; } } /** * regular expression to detect a JS template string literal */ const templateTextRegex = /(?<!\\)\${(([^\\}]|\\}|\\)*)}/; /** * regular expression to detect handlebars {{ }} */ const handlebarsRegex = /^\s*(?<!\\){{(.*)}}\s*$/; /** * Check if the given string contains embedded JS script(s) that should be executed. * @param expression The string to check * @returns true if the string contains any recognized expressions */ export function isExpressionString(expression: string): boolean { // value is template string if (templateTextRegex.test(expression)) { return true; } // value is handlebars if (handlebarsRegex.test(expression)) { return true; } // value is plain text return false; } /** * Compiles JS code embedded within a string, and then returns a callable function that will return the output of that code. * Result object is stateless and can be safely cached and reused. * * @param expression The string to compile. * @returns an EvalFunction that will return the result of the expression * @throws if the provided string contains no expressions */ export function parseExpression(expression: string): EvalFunction<unknown> { // value is template string if (templateTextRegex.test(expression)) { // parse into function return parseTemplateString(expression); } // value is handlebars const handlebarsMatches: RegExpMatchArray | null = handlebarsRegex.exec(expression); if (handlebarsMatches != null && handlebarsMatches.length === 2) { // get JS code from handlebars text const handlebarCode: string = handlebarsMatches[1]; // parse into function return parseHandlebars(handlebarCode); } throw new Error('Attempting to compile plain text as JavaScript'); } /** * Parse an ES6 template literal * * @param templateString Contents of the template string, excluding the backticks * @returns EvalFunction that will execute the template string and return a standard string * @throws If the template literal cannot be parsed */ export function parseTemplateString(templateString: string): EvalFunction<string> { // generate function body for template const functionBody = `return $$.expressionTagger\`${ templateString }\`;`; // create content return parseScript(functionBody); } /** * Parse a handlebars expression. Ex. {{ foo() }} * * @param jsString Contents of the handlebars expression, excluding the braces * @returns EvalFunction that will execute the expression and return the resulting object. * @throws If the script code cannot be parsed */ export function parseHandlebars(jsString: string): EvalFunction<unknown> { // generate body for function const functionBody = `return ${ jsString };`; // create content return parseScript(functionBody); } /** * Parse arbitrary JS code in a function context. * All JS features are available, provided that they are valid for use within a function body. * The function can optionally return a value, but return values are not type checked. * * @param functionBody JS code to execute * @returns EvalFunction that will execute the expression and return the result of the function, if any. * @throws If the JS code cannot be parsed */ export function parseScript<T>(functionBody: string): EvalFunction<T> { try { // Parse function body into callable function. // This is inherently not type-safe, as the purpose is to run unknown JS code. // eslint-disable-next-line @typescript-eslint/no-implied-eval return new Function('$', '$$', 'require', functionBody) as EvalFunction<T>; } catch (error) { throw new Error(`Parse error in function: ${ error }. Function body: ${ functionBody }`); } } /** * Calls node.js require() relatively from the Mooltipage root * @param path Path to require */ export function requireFromRoot(path: string): unknown { if (path.startsWith('./')) { path = `../../${ path }`; } else Iif (path.startsWith('.\\')) { path = `..\\..\\${ path }`; } // explicit use of require() is necessary here // eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires return require(path) as unknown; } |