Skip to content

Commit

Permalink
Bind attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
SiddharthShyniben committed Oct 23, 2021
1 parent 7f56885 commit b54db4a
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 6 deletions.
46 changes: 46 additions & 0 deletions src/bind-attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import HTMLHandler from 'https://dev.jspm.io/parse5';
import {nanoid} from 'https://deno.land/x/[email protected]/mod.ts';

export function parseAttributeBindings({HTML = '', CSS = '', JS = ''}) {
const parsedHTML = HTMLHandler.parseFragment(HTML);

function _parse(node) {
if (node.nodeName.startsWith('#') && node.nodeName !== '#document-fragment') {
return node;
}

node.childNodes = node.childNodes?.map(_parse);

if (!node.attrs) {
return node;
}

node.attrs = node.attrs.map(attr => {
const {name, value} = attr;

if (name.startsWith('{') && name.endsWith('}')) {
const cleanName = name.slice(1, -1);
const id = nanoid(5);
const eventSlug = `handle-${cleanName}-${id}`;

JS += '\n\n' + `
function __bind__attr__${cleanName}__${id}() {
document.getElementById('${eventSlug}').setAttribute('${cleanName}', ${value || cleanName});
}
`.trim();

return ({name: 'id', value: eventSlug});
}

return attr;
});

return node;
}

const out = _parse(parsedHTML);

HTML = HTMLHandler.serialize(out);

return {HTML, CSS, JS};
}
2 changes: 1 addition & 1 deletion src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function parseEvents({HTML, CSS, JS}) {

if (name.startsWith('on')) {
console.log('Got event!!');
const eventSlug = `handle-${name.slice(2)}-${attr.value.replace(/[[\](){}'"]*/gi, '')}-${nanoid(5)}`;
const eventSlug = `handle-${name.slice(2)}-${attr.value.replace(/[[\](){}'"\s;+]*/gi, '')}-${nanoid(5)}`;

JS += '\n\n';
JS += `
Expand Down
15 changes: 14 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {split} from './split.js';
import {resolve} from './resolve.js';
import {makeReactive} from './react.js';
import {parseEvents} from './events.js';
import {parseAttributeBindings} from './bind-attributes.js';

/*
* Parse a component
Expand All @@ -15,7 +16,7 @@ import {parseEvents} from './events.js';
* @return {string} code.JS - The parsed JS code
*/
export function parse(code = '', options = {wrapInHTML: true, HTMLTemplate: null}) {
let {HTML, CSS, JS} = makeReactive(parseEvents(resolve(split(code, true))));
let {HTML, CSS, JS} = makeReactive(parseAttributeBindings(parseEvents(resolve(split(code, true)))));

if (options.wrapInHTML) {
if (options.HTMLTemplate) {
Expand All @@ -41,3 +42,15 @@ ${HTML.split('\n').map(line => '\t' + line).join('\n')}
return {HTML, CSS, JS};
}

let out = parse(`<script>
let disabled = false;
let i = 0;
</script>
<button {disabled} onclick='i++; if (i > 5) disabled = true;'>Click me!</button>
`);

console.log(out.HTML);
console.log();
console.log();
console.log(out.JS);
50 changes: 46 additions & 4 deletions src/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ const {builders: b} = types;
let deps = {};
let depsInverted = false;
let HTML = '';
let ast = {};
const finalReactiveCalls = [];

export function makeReactive({HTML: HTMLIn, CSS, JS}) {
HTML = HTMLIn;

const ast = parse(JS);
ast = parse(JS);

visit(ast, {
visitUpdateExpression: visitUpdate,
visitVariableDeclaration: visitVariable
});

// It works. Don't touch
visit(ast, {
visitVariableDeclaration: secondVisitVariable
});
Expand Down Expand Up @@ -46,6 +46,7 @@ function visitVariable(path) {

visit(declaration.init, {
visitIdentifier(path) {
// O(h no)
deps[declaration.id.name].push(path.node.name);
this.traverse(path);
}
Expand Down Expand Up @@ -84,7 +85,12 @@ function secondVisitVariable(path) {
) : b.emptyStatement(),

hasBinding(HTML, declaration.id.name) ?
parse(`document.getElementById('__bind__${declaration.id.name}').innerText = ${declaration.id.name}`).program.body[0] : b.emptyStatement(), // I'm lazy
buildBinding(declaration.id.name) : b.emptyStatement(),

hasAttributeBinding(declaration.id.name) ?
b.expressionStatement(
b.callExpression(b.identifier(hasAttributeBinding(declaration.id.name)), [])
) : b.emptyStatement(),

...(deps[declaration.id.name] || []).map(
dep => b.expressionStatement(
Expand Down Expand Up @@ -161,10 +167,46 @@ function bindConsts(path) {
const reactiveDeclarators = [];

for (const declaration of path.node.declarations) {
HTML = directlyBind(HTML, declaration.id.name, eval(print(declaration.init).code));
HTML = directlyBind(HTML, declaration.id.name,
eval(print(declaration.init).code)); // TODO no eval
}

path.replace(path.node, ...reactiveDeclarators);

return false;
}

function buildBinding(name) {
return b.expressionStatement(
b.assignmentExpression(
'=',
b.memberExpression(
b.callExpression(
b.memberExpression(
b.identifier('document'),
b.identifier('getElementById')
),
[b.literal('__bind__' + name)]
),
b.identifier('innerText')
),
b.identifier(name)
)
);
}

function hasAttributeBinding(name) {
let value = '';

visit(ast, {
visitFunctionDeclaration(path) {
if (path.node.id.name.startsWith(`__bind__attr__${name}__`)) {
value = path.node.id.name;
}

return false; // There will never be a nested fn we need
}
});

return value;
}

0 comments on commit b54db4a

Please sign in to comment.