-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to run lambdas as plugin mustache extensions #951
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,3 +1,4 @@ | ||||||||||||||||||||||||||||||||||||||
import _ from "lodash"; | ||||||||||||||||||||||||||||||||||||||
import Mustache, { Context } from "mustache"; | ||||||||||||||||||||||||||||||||||||||
import { marked, Tokenizer } from "marked"; | ||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||
|
@@ -6,6 +7,7 @@ import { | |||||||||||||||||||||||||||||||||||||
renderTokens, | ||||||||||||||||||||||||||||||||||||||
Token, | ||||||||||||||||||||||||||||||||||||||
} from "@mwdb-web/commons/helpers"; | ||||||||||||||||||||||||||||||||||||||
import { fromPlugins } from "@mwdb-web/commons/plugins"; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||
* Markdown with Mustache templates for React | ||||||||||||||||||||||||||||||||||||||
|
@@ -24,6 +26,10 @@ function appendToLastElement(array: string[], value: string) { | |||||||||||||||||||||||||||||||||||||
return [...array.slice(0, -1), array[array.length - 1] + value]; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function isFunction(obj: Object): boolean { | ||||||||||||||||||||||||||||||||||||||
return typeof obj === "function"; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
function splitName(name: string) { | ||||||||||||||||||||||||||||||||||||||
if (name === ".") | ||||||||||||||||||||||||||||||||||||||
// Special case for "this" | ||||||||||||||||||||||||||||||||||||||
|
@@ -112,7 +118,12 @@ class MustacheContext extends Mustache.Context { | |||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
if (!name) return undefined; | ||||||||||||||||||||||||||||||||||||||
const path = splitName(name); | ||||||||||||||||||||||||||||||||||||||
let currentObject = this.view; | ||||||||||||||||||||||||||||||||||||||
// In case of lambdas, the subrenderer makes | ||||||||||||||||||||||||||||||||||||||
// this.view be a MustacheContext so make sure | ||||||||||||||||||||||||||||||||||||||
// we get the actual view | ||||||||||||||||||||||||||||||||||||||
let currentObject = this.view instanceof MustacheContext | ||||||||||||||||||||||||||||||||||||||
? this.view.view | ||||||||||||||||||||||||||||||||||||||
: this.view; | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+121
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's caused by incorrect overload of I think the best solution is to create root MustacheContext in renderValue instead of overloading |
||||||||||||||||||||||||||||||||||||||
for (let element of path) { | ||||||||||||||||||||||||||||||||||||||
if (!Object.prototype.hasOwnProperty.call(currentObject, element)) | ||||||||||||||||||||||||||||||||||||||
return undefined; | ||||||||||||||||||||||||||||||||||||||
|
@@ -122,8 +133,8 @@ class MustacheContext extends Mustache.Context { | |||||||||||||||||||||||||||||||||||||
this.lastValue = currentObject; | ||||||||||||||||||||||||||||||||||||||
if (searchable) { | ||||||||||||||||||||||||||||||||||||||
if ( | ||||||||||||||||||||||||||||||||||||||
typeof currentObject === "object" || | ||||||||||||||||||||||||||||||||||||||
typeof currentObject === "function" | ||||||||||||||||||||||||||||||||||||||
isFunction(currentObject) || | ||||||||||||||||||||||||||||||||||||||
typeof currentObject === "object" | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
// Non-primitives are not directly searchable | ||||||||||||||||||||||||||||||||||||||
return undefined; | ||||||||||||||||||||||||||||||||||||||
|
@@ -135,6 +146,9 @@ class MustacheContext extends Mustache.Context { | |||||||||||||||||||||||||||||||||||||
if (!query) return undefined; | ||||||||||||||||||||||||||||||||||||||
return new SearchReference(query, currentObject); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
if (isFunction(currentObject)) { | ||||||||||||||||||||||||||||||||||||||
currentObject = currentObject.call(this.view); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see that object returned by lookup is already called by Mustache.js as a part of https://github.com/janl/mustache.js/blob/master/mustache.js#L609 |
||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
return currentObject; | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
@@ -212,8 +226,43 @@ class MarkedTokenizer extends Tokenizer { | |||||||||||||||||||||||||||||||||||||
const mustacheWriter = new MustacheWriter(); | ||||||||||||||||||||||||||||||||||||||
const markedTokenizer = new MarkedTokenizer(); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
type stringOpFunc = (I: string) => string; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const lambda = (func: stringOpFunc = _.identity) => { | ||||||||||||||||||||||||||||||||||||||
// A factory method for custom lambdas. | ||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||
// The inner method receives the text inside the section | ||||||||||||||||||||||||||||||||||||||
// and the subrenderer. It then renders the value, and passes | ||||||||||||||||||||||||||||||||||||||
// it to a user defined function. | ||||||||||||||||||||||||||||||||||||||
// | ||||||||||||||||||||||||||||||||||||||
// E.g.: ("{{name}}", renderer) => "John Doe" | ||||||||||||||||||||||||||||||||||||||
// (func is toUpperCase) => "JOHN DOE" | ||||||||||||||||||||||||||||||||||||||
return () => function(text: string, renderer: any): string { | ||||||||||||||||||||||||||||||||||||||
return func(renderer(text.trim())); | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
const lambdas = fromPlugins("mustacheExtensions").reduce( | ||||||||||||||||||||||||||||||||||||||
(prev, curr) => { | ||||||||||||||||||||||||||||||||||||||
return { | ||||||||||||||||||||||||||||||||||||||
...prev, | ||||||||||||||||||||||||||||||||||||||
..._.mapValues(curr, (func: stringOpFunc) => lambda(func)) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
{}); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
Comment on lines
+245
to
+253
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Global scope is too early for call to I think it should be moved to the renderValue scope. |
||||||||||||||||||||||||||||||||||||||
export function renderValue(template: string, value: Object, options: Option) { | ||||||||||||||||||||||||||||||||||||||
const markdown = mustacheWriter.render(template, value); | ||||||||||||||||||||||||||||||||||||||
const markdown = mustacheWriter.render( | ||||||||||||||||||||||||||||||||||||||
template, | ||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||
...value, | ||||||||||||||||||||||||||||||||||||||
"value": | ||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||
...(value as any).value, | ||||||||||||||||||||||||||||||||||||||
...lambdas | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+255
to
+265
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
The best solution would be a dedicated collection in MustacheContext. |
||||||||||||||||||||||||||||||||||||||
const tokens = marked.lexer(markdown, { | ||||||||||||||||||||||||||||||||||||||
...marked.defaults, | ||||||||||||||||||||||||||||||||||||||
tokenizer: markedTokenizer, | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use lodash
_.isFunction
for that, definition is the same 😄 https://github.com/lodash/lodash/blob/a67a085cc0612f5b83d78024e507427dab25ca2c/src/isFunction.ts#L28Meanwhile I found that lodash is not really maintained anymore 🤔