khufu
Version:
A template language for incremental-dom or DSL for javascript views
588 lines (520 loc) • 22.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
exports.compile = compile;
var _babelCore = require("babel-core");
var babel = _interopRequireWildcard(_babelCore);
var _babelTypes = require("babel-types");
var T = _interopRequireWildcard(_babelTypes);
var _compile_view = require("./compile_view.js");
var _compile_expression = require("./compile_expression.js");
var expression = _interopRequireWildcard(_compile_expression);
var _babelUtil = require("./babel-util");
var _compiler = require("./compiler");
var _vars = require("./vars");
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function optimize_plus(a, b) {
if (a[0] == 'string' && b[0] == 'string') {
return ['string', a[1] + b[1]];
}
return ['binop', '+', a, b];
}
function sort_attributes(attributes, elname, opt) {
var stat = [];
var dyn = [];
var cls_stat = [];
var cls_dyn = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = attributes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var _step$value = _slicedToArray(_step.value, 2),
name = _step$value[0],
value = _step$value[1];
var is_static = false;
/// TODO(tailhook) maybe employ more deep analysis?
switch (value && value[0]) {
case undefined:
case 'string':
case 'number':
is_static = !!opt.static_attrs;
break;
default:
break;
}
if (name == 'class') {
(is_static ? cls_stat : cls_dyn).push(value);
} else {
(is_static ? stat : dyn).push([name, value]);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (opt.additional_class && (cls_stat.length || cls_dyn.length || opt.always_add_class.has(elname))) {
cls_stat.unshift(['string', opt.additional_class]);
}
if (cls_stat.length && !cls_dyn.length) {
if (opt.static_attrs) {
stat.push(['class', ['string', cls_stat.map(function (x) {
return x[1];
}).join(' ')]]);
} else {
dyn.push(['class', ['string', cls_stat.map(function (x) {
return x[1];
}).join(' ')]]);
}
} else if (cls_stat.length && cls_dyn.length) {
dyn.push(['class', cls_dyn.reduce(function (x, y) {
return optimize_plus(optimize_plus(x, ['string', ' ']), y);
}, ['string', cls_stat.map(function (x) {
return x[1];
}).join(' ')])]);
} else if (cls_dyn.length) {
dyn.push(['class', cls_dyn.slice(1).reduce(function (x, y) {
return optimize_plus(optimize_plus(x, ['string', ' ']), y);
}, cls_dyn[0])]);
}
return [stat, dyn];
}
function insert_static(elname, attributes, path, opt) {
var topscope = path.scope;
while (topscope.parent) {
topscope = topscope.parent;
}
var array = [];
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = attributes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _step2$value = _slicedToArray(_step2.value, 2),
aname = _step2$value[0],
value = _step2$value[1];
array.push(T.stringLiteral(aname));
if (value == undefined) {
array.push(T.stringLiteral(aname));
} else {
array.push(expression.compile(value, path, opt));
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var ident = topscope.generateUidIdentifier(elname.toUpperCase() + '_ATTRS');
topscope.push({
id: ident,
init: T.arrayExpression(array),
kind: 'let' });
return ident;
}
function insert_stores(elname, stores, genattrs, path, opt) {
var local_stores = new Map();
var stores_id = path.scope.generateUidIdentifier(elname + '_stores');
genattrs.push(['__stores', T.objectExpression(stores.map(function (_ref) {
var _ref2 = _slicedToArray(_ref, 4),
_store = _ref2[0],
name = _ref2[1],
value = _ref2[2],
middlewares = _ref2[3];
local_stores.set(name, T.memberExpression(stores_id, T.identifier(name)));
return T.objectProperty(T.identifier(name), T.functionExpression(null, [], T.blockStatement([
// TODO(tailhook) pass path to inner function
T.returnStatement(T.arrayExpression([expression.compile(value, path, opt), T.arrayExpression(middlewares.map(function (m) {
return expression.compile(m, path, opt);
}))]))])));
}))]);
return [stores_id, local_stores];
}
function insert_links(links, genattrs, local_stores, path, opt) {
var map = new Map();
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = links[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var _step3$value = _slicedToArray(_step3.value, 4),
_link = _step3$value[0],
names = _step3$value[1],
action = _step3$value[2],
target = _step3$value[3];
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = names[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var name = _step6.value;
if (map.has(name)) {
map.get(name).push([action, target]);
} else {
map.set(name, [[action, target]]);
}
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
}
/// First visiting single-handler events
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = links[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var _step4$value = _slicedToArray(_step4.value, 4),
_link = _step4$value[0],
names = _step4$value[1],
action = _step4$value[2],
target = _step4$value[3];
var single_refcnt_names = names.filter(function (x) {
return map.get(x).length == 1;
});
if (single_refcnt_names.length == 0) continue;
var fid = path.scope.generateUidIdentifier('ln_' + single_refcnt_names.join('_'));
var fun = T.functionDeclaration(fid, [T.identifier('event')], T.blockStatement([]));
var fpath = (0, _babelUtil.push_to_body)(path, fun).get('body');
console.assert(target[0] == 'store');
var store = (0, _vars.get_var)(path, target[1]);
if (!store) {
store = local_stores.get(target[1]);
if (!store) {
throw (0, _compiler.parse_tree_error)("Unknown store: " + target[1], target);
}
}
(0, _vars.set_var)(fpath, 'this', T.identifier('this'));
(0, _vars.set_var)(fpath, 'event', T.identifier('event'));
(0, _babelUtil.push_to_body)(fpath, T.callExpression(T.memberExpression(store, T.identifier('dispatch')), [expression.compile(action, fpath, opt)]));
var _iteratorNormalCompletion7 = true;
var _didIteratorError7 = false;
var _iteratorError7 = undefined;
try {
for (var _iterator7 = single_refcnt_names[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
var _name = _step7.value;
genattrs.push(['on' + _name, fid]);
}
} catch (err) {
_didIteratorError7 = true;
_iteratorError7 = err;
} finally {
try {
if (!_iteratorNormalCompletion7 && _iterator7.return) {
_iterator7.return();
}
} finally {
if (_didIteratorError7) {
throw _iteratorError7;
}
}
}
}
/// For now multiple handler events we create for each event separately
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = map.entries()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var _step5$value = _slicedToArray(_step5.value, 2),
_name2 = _step5$value[0],
handlers = _step5$value[1];
if (handlers.length == 1) continue;
var _fid = path.scope.generateUidIdentifier('ln_' + _name2);
var _fun = T.functionDeclaration(_fid, [T.identifier('event')], T.blockStatement([]));
var _fpath = (0, _babelUtil.push_to_body)(path, _fun).get('body');
var _iteratorNormalCompletion8 = true;
var _didIteratorError8 = false;
var _iteratorError8 = undefined;
try {
for (var _iterator8 = handlers[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
var _step8$value = _slicedToArray(_step8.value, 2),
action = _step8$value[0],
target = _step8$value[1];
console.assert(target[0] == 'store');
var _store2 = (0, _vars.get_var)(path, target[1]);
if (!_store2) {
throw (0, _compiler.parse_tree_error)("Unknown store: " + target[1], handlers);
}
(0, _vars.set_var)(_fpath, 'this', T.identifier('this'));
(0, _vars.set_var)(_fpath, 'event', T.identifier('event'));
(0, _babelUtil.push_to_body)(_fpath, T.callExpression(T.memberExpression(_store2, T.identifier('dispatch')), [expression.compile(action, _fpath, opt)]));
}
} catch (err) {
_didIteratorError8 = true;
_iteratorError8 = err;
} finally {
try {
if (!_iteratorNormalCompletion8 && _iterator8.return) {
_iterator8.return();
}
} finally {
if (_didIteratorError8) {
throw _iteratorError8;
}
}
}
genattrs.push(['on' + _name2, _fid]);
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
function compile(element, path, opt, key) {
var _element2 = _slicedToArray(element, 5),
_element = _element2[0],
name = _element2[1],
classes = _element2[2],
attributes = _element2[3],
children = _element2[4];
var links = children.filter(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 1),
x = _ref4[0];
return x == 'link';
});
var stores = children.filter(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 1),
x = _ref6[0];
return x == 'store';
});
var body = children.filter(function (_ref7) {
var _ref8 = _slicedToArray(_ref7, 1),
x = _ref8[0];
return x != 'link' && x != 'store';
});
if (classes.length) {
var _iteratorNormalCompletion9 = true;
var _didIteratorError9 = false;
var _iteratorError9 = undefined;
try {
for (var _iterator9 = classes[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
var _step9$value = _slicedToArray(_step9.value, 2),
cls = _step9$value[0],
cond = _step9$value[1];
if (cond) {
attributes.push(['class', ['if', cond, ['string', cls], ['string', '']]]);
} else {
attributes.push(['class', ['string', cls]]);
}
}
} catch (err) {
_didIteratorError9 = true;
_iteratorError9 = err;
} finally {
try {
if (!_iteratorNormalCompletion9 && _iterator9.return) {
_iterator9.return();
}
} finally {
if (_didIteratorError9) {
throw _iteratorError9;
}
}
}
}
var _sort_attributes = sort_attributes(attributes, name, opt),
_sort_attributes2 = _slicedToArray(_sort_attributes, 2),
stat = _sort_attributes2[0],
dyn = _sort_attributes2[1];
attributes = dyn;
var attrib_expr = void 0;
if (stat.length) {
attrib_expr = insert_static(name, stat, path, opt);
}
var genattrs = [];
var local_stores = new Map();
var stores_id = void 0;
if (stores.length) {
var _insert_stores = insert_stores(name, stores, genattrs, path, opt);
var _insert_stores2 = _slicedToArray(_insert_stores, 2);
stores_id = _insert_stores2[0];
local_stores = _insert_stores2[1];
}
if (links.length) {
insert_links(links, genattrs, local_stores, path, opt);
}
var attribs = [T.stringLiteral(name),
// We need to add a tag name to the element, because incremental-dom
// throws an exception when tag name is changed. Changing tag
// name is is only possible with hot-reload, though.
//
// TODO(tailhook) should we do it only if hot-reload is enabled?
(0, _compile_view.join_key)(key, T.stringLiteral('-' + name))];
if (attributes.length || genattrs.length) {
attribs.push(attrib_expr || T.nullLiteral());
var _iteratorNormalCompletion10 = true;
var _didIteratorError10 = false;
var _iteratorError10 = undefined;
try {
for (var _iterator10 = attributes[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
var _step10$value = _slicedToArray(_step10.value, 2),
aname = _step10$value[0],
value = _step10$value[1];
attribs.push(T.stringLiteral(aname));
if (value) {
attribs.push(expression.compile(value, path, opt));
} else {
attribs.push(T.stringLiteral(aname));
}
}
} catch (err) {
_didIteratorError10 = true;
_iteratorError10 = err;
} finally {
try {
if (!_iteratorNormalCompletion10 && _iterator10.return) {
_iterator10.return();
}
} finally {
if (_didIteratorError10) {
throw _iteratorError10;
}
}
}
var _iteratorNormalCompletion11 = true;
var _didIteratorError11 = false;
var _iteratorError11 = undefined;
try {
for (var _iterator11 = genattrs[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
var _step11$value = _slicedToArray(_step11.value, 2),
aname = _step11$value[0],
value = _step11$value[1];
attribs.push(T.stringLiteral(aname));
attribs.push(value);
}
} catch (err) {
_didIteratorError11 = true;
_iteratorError11 = err;
} finally {
try {
if (!_iteratorNormalCompletion11 && _iterator11.return) {
_iterator11.return();
}
} finally {
if (_didIteratorError11) {
throw _iteratorError11;
}
}
}
} else if (attrib_expr) {
attribs.push(attrib_expr);
}
if (body.length == 0 && stores.length == 0) {
var node = T.callExpression(T.identifier('elementVoid'), attribs);
path.node.body.push(T.expressionStatement(node));
} else {
var el = T.callExpression(T.identifier('elementOpen'), attribs);
if (stores_id) {
el = T.variableDeclaration('let', [T.variableDeclarator(stores_id, T.memberExpression(el, T.identifier('__stores')))]);
} else {
el = T.expressionStatement(el);
}
(0, _babelUtil.push_to_body)(path, el);
var blockpath = (0, _babelUtil.push_to_body)(path, T.blockStatement([]));
if (stores_id) {
var _iteratorNormalCompletion12 = true;
var _didIteratorError12 = false;
var _iteratorError12 = undefined;
try {
for (var _iterator12 = local_stores[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
var _step12$value = _slicedToArray(_step12.value, 2),
_name3 = _step12$value[0],
expr = _step12$value[1];
(0, _vars.set_var)(blockpath, _name3, expr);
}
} catch (err) {
_didIteratorError12 = true;
_iteratorError12 = err;
} finally {
try {
if (!_iteratorNormalCompletion12 && _iterator12.return) {
_iterator12.return();
}
} finally {
if (_didIteratorError12) {
throw _iteratorError12;
}
}
}
}
(0, _compile_view.compile_body)(body, blockpath, opt);
/// Optimize the scope without variables
if (Object.keys(blockpath.scope.bindings).length == 0) {
blockpath.replaceWithMultiple(blockpath.get('body').map(function (x) {
return x.node;
}));
}
path.node.body.push(T.expressionStatement(T.callExpression(T.identifier('elementClose'), [T.stringLiteral(name)])));
}
}