UNPKG

khufu

Version:

A template language for incremental-dom or DSL for javascript views

588 lines (520 loc) 22.2 kB
"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)]))); } }