Skip to content

Instantly share code, notes, and snippets.

@saenai255
Last active November 10, 2020 16:33
Show Gist options
  • Save saenai255/beaee90e38d7c4d2dbef450a9e2a3ed9 to your computer and use it in GitHub Desktop.
Save saenai255/beaee90e38d7c4d2dbef450a9e2a3ed9 to your computer and use it in GitHub Desktop.
jtl-parser
toHTML = (root, instance) => {
const TOKEN = {
IF: ':if',
FOR: ':for',
FOR_OF: ' of ',
EVENT: {
CLICK: ':click'
}
}
const generateEvents = node => {
if (!node.attributes || !node.attributes[TOKEN.EVENT.CLICK]) {
return;
}
const fn = node.attributes[TOKEN.EVENT.CLICK].value;
let callableFn;
const scope = Object.entries(instance).map(([key, value]) => `const ${key} = ${typeof value === 'function' ? value : JSON.stringify(value)}`).join(';');
try {
const mapToCallableFunction = fn => {
const isClassMethod = /[a-zA-Z]\w+$/gm.test(fn);
if (isClassMethod) {
if (fn.includes('this.')) {
return `(...arg) => ` + fn + '(...arg)';
}
return '(...arg) => this.' + fn + '(...arg)';
}
return fn;
};
const fnTemplate = `
(function(...args) {
try {
const eventHandler = ${mapToCallableFunction(fn)};
return eventHandler(...args);
} catch(e) {
if (e instanceof ReferenceError) {
console.error('[${instance.tag}] Cannot invoke event handler.', {handler: "${mapToCallableFunction(fn)}", props: args, componentInstance: this });
throw new ReferenceError();
}
throw e;
}
})`;
callableFn = eval(fnTemplate);
} catch(e) {
throw e;
}
try {
node.addEventListener('click', callableFn.bind(instance));
} catch(e) {
if (e instanceof TypeError) {
console.error(`[${instance.tag}] Cannot parse event handler.`, { handler: fn, componentInstance: instance });
throw new TypeError();
}
throw e;
}
}
const generateVariables = node => {
if (!node.innerText || !node.innerText.match(/({{\s?)([a-zA-Z0-9_]+)(\s?}})/g)) {
return;
}
const [interpolation, _, varName] = node.innerText.matchAll(/({{\s?)([a-zA-Z0-9_]+)(\s?}})/g).next().value;
node.innerText = node.innerText.replaceAll(interpolation, instance[varName]);
}
const generateIfs = node => {
if (!node.attributes || !node.attributes[TOKEN.IF]) {
return;
}
if (!eval(node.attributes[TOKEN.IF].value)) {
node.parentNode.removeChild(node);
}
node.attributes.removeNamedItem(TOKEN.IF)
}
const generateFors = node => {
if (!node.attributes || !node.attributes[TOKEN.FOR]) {
return;
}
const parent = node.parentNode;
const forTemplate = node.attributes[TOKEN.FOR].value;
const [varName, arrayName] = forTemplate.split(TOKEN.FOR_OF);
const array = instance[arrayName];
for (let value of array) {
const newNode = node.cloneNode(true);
parent.insertBefore(newNode, node);
newNode.attributes.removeNamedItem(TOKEN.FOR);
newNode.outerHTML = newNode.outerHTML.replace(new RegExp(`{{ ${varName} }}`,'g'), value);
newNode.innerHTML = node.innerHTML;
}
parent.removeChild(node);
}
const generators = [generateIfs, generateFors, generateVariables, generateEvents];
[...root.childNodes].forEach(child => generators.forEach(generator => generator(child)));
return root;
}
render = (props) => {
const { template, tag, variables } = props;
if (!props.__nativeElement) {
props.__nativeElement = document.createElement(tag)
}
const element = props.__nativeElement;
element.innerHTML = template.trim();
// for (const key of Object.keys(handlers)) {
// props.handlers[key] = props.handlers[key].bind(props)
// }
const html = toHTML(element, props);
props.onInit && props.onInit();
return html;
}
const instance = new(class {
tag = 'x-nav';
template = `
<html>
hello world
<p :if="1 + 2 == 3">Showing</p>
<p :if="1 + 2 == 4">NOT Showing</p>
<a href="/{{ link }}" :for="link of links" style="display: block;">My link to {{ link }}</a>
<button :click="event => this.hello('test')">Clicky</button>
<div>{{ count }}</div>
<button :click="increment">Add</button>
</html>
`;
count = 0;
links = ['home', 'login'];
increment = () => {
this.count++;
render(this);
}
hello = event => {
console.log(event);
console.log(this.links);
console.log(this);
this.sayHello();
}
sayHello = () => {
log('hello')
}
onInit = () => {
setTimeout(() => {
this.count++;
render(this);
}, 1000);
}
});
elem = render(instance);
document.body.innerHTML = '';
document.body.appendChild(elem);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment