Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize #38

Closed
jorgebucaran opened this issue Feb 6, 2017 · 36 comments
Closed

Modernize #38

jorgebucaran opened this issue Feb 6, 2017 · 36 comments

Comments

@jorgebucaran
Copy link
Owner

jorgebucaran commented Feb 6, 2017


TL;DR 馃憢

If you are reading this issue now, just be aware we considered rewriting the source in ES6 at some point, but eventually decided not to. The rationale is ES6 would make it slightly more difficult for developers to hack with Hyperapp without introducing a build system and still no CommonJS support out of the box.

Hyperapp is a minimal library and with an unchanging goal of staying forever lean and beginner friendly.

You can take an hour and read through the entire 250ish LOC here and here and you'd understand pretty much everything. Zero dependency. It's all there, the virtual DOM, the state container, etc.


See original discussion
@maraisr Summarizing the discussion in #29, #30, #31 and #32 we'd like to modernize the code base to use more ES6 idioms and introduce webpack to bundle the distribution.

Rationale

  • Using more ES6 idioms may encourage more contributions.

  • Using more ES6 idioms means a smaller bundle for users targetting really modern browsers. For example, in production users may consume our src directory directly with their application and don't babelify (or maybe babelify a some things via plugins) and use something like babili to minify modern JavaScript. This means even smaller bundles.

  • Other than prototyping, examples, presentations and maybe even real small-ish apps, users will want to bundle their apps using a bundler like browserify, webpack, rollup, etc.

@jorgebucaran jorgebucaran added docs You read it here first NEXT discussion labels Feb 6, 2017
@jorgebucaran jorgebucaran mentioned this issue Feb 6, 2017
@SkaterDad
Copy link
Contributor

Did you consider using Rollup instead of Webpack? It seems to produce smaller builds.

As a proof of concept, this is the app.js bundle without minification or Babili applied. Webpack adds quite a lot more overhead to each of the bundles.

Webpack output

var app =
/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};

/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {

/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;

/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};

/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/ 		// Flag the module as loaded
/******/ 		module.l = true;

/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}


/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;

/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;

/******/ 	// identity function for calling harmony imports with the correct context
/******/ 	__webpack_require__.i = function(value) { return value; };

/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};

/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};

/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";

/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ({

/***/ 3:
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var _arguments = arguments;

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

console.log("DEV");
module.exports = function (options) {
	var defer = function defer(fn, data) {
		setTimeout(function (_) {
			return fn(data);
		}, 0);
	};

	var merge = function merge(a, b) {
		var obj = {},
		    key;

		if (isPrimitive(typeof b === "undefined" ? "undefined" : _typeof(b)) || Array.isArray(b)) {
			return b;
		}

		for (key in a) {
			obj[key] = a[key];
		}
		for (key in b) {
			obj[key] = b[key];
		}

		return obj;
	};

	var isPrimitive = function isPrimitive(type) {
		return type === "string" || type === "number" || type === "boolean";
	};

	var render = function render(model, view, lastNode) {
		patch(root, node = view(model, msg), lastNode, 0);
	};

	var shouldUpdate = function shouldUpdate(a, b) {
		return a.tag !== b.tag || (typeof a === "undefined" ? "undefined" : _typeof(a)) !== (typeof b === "undefined" ? "undefined" : _typeof(b)) || isPrimitive(typeof a === "undefined" ? "undefined" : _typeof(a)) && a !== b;
	};

	var patch = function patch(parent, node, oldNode, index) {
		if (oldNode === undefined) {
			parent.appendChild(createElementFrom(node));
		} else if (node === undefined) {
			while (index > 0 && !parent.childNodes[index]) {
				index--;
			}

			if (index >= 0) {
				var element = parent.childNodes[index];

				if (oldNode && oldNode.data) {
					var hook = oldNode.data.onremove;
					if (hook) {
						defer(hook, element);
					}
				}

				parent.removeChild(element);
			}
		} else if (shouldUpdate(node, oldNode)) {
			parent.replaceChild(createElementFrom(node), parent.childNodes[index]);
		} else if (node.tag) {
			var element = parent.childNodes[index];

			updateElementData(element, node.data, oldNode.data);

			var len = node.tree.length,
			    oldLen = oldNode.tree.length;

			for (var i = 0; i < len || i < oldLen; i++) {
				patch(element, node.tree[i], oldNode.tree[i], i);
			}
		}
	};

	var createElementFrom = function createElementFrom(node) {
		var element;

		if (isPrimitive(typeof node === "undefined" ? "undefined" : _typeof(node))) {
			element = document.createTextNode(node);
		} else {
			element = node.data && node.data.ns ? document.createElementNS(node.data.ns, node.tag) : document.createElement(node.tag);

			for (var name in node.data) {
				if (name === "oncreate") {
					defer(node.data[name], element);
				} else {
					setElementData(element, name, node.data[name]);
				}
			}

			for (var i = 0; i < node.tree.length; i++) {
				element.appendChild(createElementFrom(node.tree[i]));
			}
		}

		return element;
	};

	var setElementData = function setElementData(element, name, value, oldValue) {
		if (name === "style") {
			for (var i in value) {
				element.style[i] = value[i];
			}
		} else if (name.substr(0, 2) === "on") {
			var event = name.substr(2);
			element.removeEventListener(event, oldValue);
			element.addEventListener(event, value);
		} else {
			if (value === "false" || value === false) {
				element.removeAttribute(name);
				element[name] = false;
			} else {
				element.setAttribute(name, value);
				element[name] = value;
			}
		}
	};

	var removeElementData = function removeElementData(element, name, value) {
		element.removeAttribute(name === "className" ? "class" : name);

		if (typeof value === "boolean" || value === "true" || value === "false") {
			element[name] = false;
		}
	};

	var updateElementData = function updateElementData(element, data, oldData) {
		for (var name in merge(oldData, data)) {
			var value = data[name],
			    oldValue = oldData[name];

			if (value === undefined) {
				removeElementData(element, name, oldValue);
			} else if (value !== oldValue) {
				name === "onupdate" ? defer(value, element) : setElementData(element, name, value, oldValue);
			}
		}
	};

	var regexify = function regexify(path) {
		var keys = [],
		    re = "^" + path.replace(/\//g, "\\/").replace(/:([A-Za-z0-9_]+)/g, function (_, key) {
			keys.push(key);
			return "([A-Za-z0-9_]+)";
		}) + "/?$";

		return { re: re, keys: keys };
	};

	var route = function route(routes, path) {
		for (var route in routes) {
			var re = regexify(route),
			    params = {},
			    match;

			path.replace(new RegExp(re.re, "g"), function (_) {
				for (var i = 1; i < _arguments.length - 2; i++) {
					params[re.keys.shift()] = _arguments[i];
				}
				match = function match(model, msg) {
					return routes[route](model, msg, params);
				};
			});

			if (match) {
				return match;
			}
		}

		return routes["/"];
	};

	var msg = {};

	var model = options.model;
	var reducers = options.update || {};
	var effects = options.effects || {};
	var subs = options.subs || {};

	var hooks = merge({
		onAction: Function.prototype,
		onUpdate: Function.prototype,
		onError: function onError(err) {
			throw err;
		}
	}, options.hooks);

	var node;
	var root = options.root || document.body.appendChild(document.createElement("div"));
	var view = options.view || function (_) {
		return root;
	};
	var routes = typeof view === "function" ? undefined : view;

	if (routes) {
		view = route(routes, location.pathname);

		msg.setLocation = function (data) {
			render(model, view = route(routes, data), node);
			history.pushState({}, "", data);
		};

		window.addEventListener("popstate", function (_) {
			render(model, view = route(routes, location.pathname), node);
		});

		window.addEventListener("click", function (e) {
			if (e.metaKey || e.shiftKey || e.ctrlKey || e.altKey) {
				return;
			}

			var target = e.target;

			while (target && target.localName !== "a") {
				target = target.parentNode;
			}

			if (target && target.host === location.host && !target.hasAttribute("data-no-routing")) {

				var element = target.hash === "" ? element : document.querySelector(target.hash);

				if (element) {
					element.scrollIntoView(true);
				} else {
					msg.setLocation(target.pathname);
					e.preventDefault();
				}
			}
		});
	}

	var _loop = function _loop(name) {
		msg[name] = function (data) {
			hooks.onAction(name, data);

			var effect = effects[name];
			if (effect) {
				return effect(model, msg, data, hooks.onError);
			}

			var update = reducers[name],
			    _model = model;
			render(model = merge(model, update(model, data)), view, node);

			hooks.onUpdate(_model, model, data);
		};
	};

	for (var name in merge(reducers, effects)) {
		_loop(name);
	}

	document.addEventListener("DOMContentLoaded", function (_) {
		for (var sub in subs) {
			subs[sub](model, msg, hooks.onError);
		}
	});

	render(model, view);
};

/***/ })

/******/ });

Rollup Output

var app = (function () {
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  return typeof obj;
} : function (obj) {
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};

var _arguments = arguments;
console.log("DEV");
var app = (function (options) {
	var defer = function defer(fn, data) {
		setTimeout(function (_) {
			return fn(data);
		}, 0);
	};

	var merge = function merge(a, b) {
		var obj = {},
		    key;

		if (isPrimitive(typeof b === "undefined" ? "undefined" : _typeof(b)) || Array.isArray(b)) {
			return b;
		}

		for (key in a) {
			obj[key] = a[key];
		}
		for (key in b) {
			obj[key] = b[key];
		}

		return obj;
	};

	var isPrimitive = function isPrimitive(type) {
		return type === "string" || type === "number" || type === "boolean";
	};

	var render = function render(model, view, lastNode) {
		patch(root, node = view(model, msg), lastNode, 0);
	};

	var shouldUpdate = function shouldUpdate(a, b) {
		return a.tag !== b.tag || (typeof a === "undefined" ? "undefined" : _typeof(a)) !== (typeof b === "undefined" ? "undefined" : _typeof(b)) || isPrimitive(typeof a === "undefined" ? "undefined" : _typeof(a)) && a !== b;
	};

	var patch = function patch(parent, node, oldNode, index) {
		if (oldNode === undefined) {
			parent.appendChild(createElementFrom(node));
		} else if (node === undefined) {
			while (index > 0 && !parent.childNodes[index]) {
				index--;
			}

			if (index >= 0) {
				var element = parent.childNodes[index];

				if (oldNode && oldNode.data) {
					var hook = oldNode.data.onremove;
					if (hook) {
						defer(hook, element);
					}
				}

				parent.removeChild(element);
			}
		} else if (shouldUpdate(node, oldNode)) {
			parent.replaceChild(createElementFrom(node), parent.childNodes[index]);
		} else if (node.tag) {
			var element = parent.childNodes[index];

			updateElementData(element, node.data, oldNode.data);

			var len = node.tree.length,
			    oldLen = oldNode.tree.length;

			for (var i = 0; i < len || i < oldLen; i++) {
				patch(element, node.tree[i], oldNode.tree[i], i);
			}
		}
	};

	var createElementFrom = function createElementFrom(node) {
		var element;

		if (isPrimitive(typeof node === "undefined" ? "undefined" : _typeof(node))) {
			element = document.createTextNode(node);
		} else {
			element = node.data && node.data.ns ? document.createElementNS(node.data.ns, node.tag) : document.createElement(node.tag);

			for (var name in node.data) {
				if (name === "oncreate") {
					defer(node.data[name], element);
				} else {
					setElementData(element, name, node.data[name]);
				}
			}

			for (var i = 0; i < node.tree.length; i++) {
				element.appendChild(createElementFrom(node.tree[i]));
			}
		}

		return element;
	};

	var setElementData = function setElementData(element, name, value, oldValue) {
		if (name === "style") {
			for (var i in value) {
				element.style[i] = value[i];
			}
		} else if (name.substr(0, 2) === "on") {
			var event = name.substr(2);
			element.removeEventListener(event, oldValue);
			element.addEventListener(event, value);
		} else {
			if (value === "false" || value === false) {
				element.removeAttribute(name);
				element[name] = false;
			} else {
				element.setAttribute(name, value);
				element[name] = value;
			}
		}
	};

	var removeElementData = function removeElementData(element, name, value) {
		element.removeAttribute(name === "className" ? "class" : name);

		if (typeof value === "boolean" || value === "true" || value === "false") {
			element[name] = false;
		}
	};

	var updateElementData = function updateElementData(element, data, oldData) {
		for (var name in merge(oldData, data)) {
			var value = data[name],
			    oldValue = oldData[name];

			if (value === undefined) {
				removeElementData(element, name, oldValue);
			} else if (value !== oldValue) {
				name === "onupdate" ? defer(value, element) : setElementData(element, name, value, oldValue);
			}
		}
	};

	var regexify = function regexify(path) {
		var keys = [],
		    re = "^" + path.replace(/\//g, "\\/").replace(/:([A-Za-z0-9_]+)/g, function (_, key) {
			keys.push(key);
			return "([A-Za-z0-9_]+)";
		}) + "/?$";

		return { re: re, keys: keys };
	};

	var route = function route(routes, path) {
		for (var route in routes) {
			var re = regexify(route),
			    params = {},
			    match;

			path.replace(new RegExp(re.re, "g"), function (_) {
				for (var i = 1; i < _arguments.length - 2; i++) {
					params[re.keys.shift()] = _arguments[i];
				}
				match = function match(model, msg) {
					return routes[route](model, msg, params);
				};
			});

			if (match) {
				return match;
			}
		}

		return routes["/"];
	};

	var msg = {};

	var model = options.model;
	var reducers = options.update || {};
	var effects = options.effects || {};
	var subs = options.subs || {};

	var hooks = merge({
		onAction: Function.prototype,
		onUpdate: Function.prototype,
		onError: function onError(err) {
			throw err;
		}
	}, options.hooks);

	var node;
	var root = options.root || document.body.appendChild(document.createElement("div"));
	var view = options.view || function (_) {
		return root;
	};
	var routes = typeof view === "function" ? undefined : view;

	if (routes) {
		view = route(routes, location.pathname);

		msg.setLocation = function (data) {
			render(model, view = route(routes, data), node);
			history.pushState({}, "", data);
		};

		window.addEventListener("popstate", function (_) {
			render(model, view = route(routes, location.pathname), node);
		});

		window.addEventListener("click", function (e) {
			if (e.metaKey || e.shiftKey || e.ctrlKey || e.altKey) {
				return;
			}

			var target = e.target;

			while (target && target.localName !== "a") {
				target = target.parentNode;
			}

			if (target && target.host === location.host && !target.hasAttribute("data-no-routing")) {

				var element = target.hash === "" ? element : document.querySelector(target.hash);

				if (element) {
					element.scrollIntoView(true);
				} else {
					msg.setLocation(target.pathname);
					e.preventDefault();
				}
			}
		});
	}

	var _loop = function _loop(name) {
		msg[name] = function (data) {
			hooks.onAction(name, data);

			var effect = effects[name];
			if (effect) {
				return effect(model, msg, data, hooks.onError);
			}

			var update = reducers[name],
			    _model = model;
			render(model = merge(model, update(model, data)), view, node);

			hooks.onUpdate(_model, model, data);
		};
	};

	for (var name in merge(reducers, effects)) {
		_loop(name);
	}

	document.addEventListener("DOMContentLoaded", function (_) {
		for (var sub in subs) {
			subs[sub](model, msg, hooks.onError);
		}
	});

	render(model, view);
});

return app;

}());
//# sourceMappingURL=app.build.js.map

The only change in app.js was changing module.exports = to export default .

Rollup config was pretty basic just to prove the concept. Naturally you can extend this with minification and such.

import babel from 'rollup-plugin-babel';
import babelrc from 'babelrc-rollup';

let pkg = require('./package.json');
let external = Object.keys(pkg.dependencies);

export default {
  entry: 'src/app.js',
  plugins: [
    babel(babelrc()),
  ],
  external: external,
  targets: [
    {
      dest: 'dist/app.build.js',
      format: 'iife',
      moduleName: 'app',
      sourceMap: true
    }
  ]
};

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 6, 2017

@SkaterDad There was no consideration since I'm not versed particularly in either rollup or webpack, but I'll take whichever can produce smaller bundles.

@tunnckoCore
Copy link

tunnckoCore commented Feb 6, 2017

Btw, I don't think iife is the right thing.

@SkaterDad 馃憤 thought to mention that too.

edit: Babili isn't ready enough, and uglify is still better and gives smaller sizes.

@SkaterDad
Copy link
Contributor

SkaterDad commented Feb 6, 2017

@tunnckoCore I used iife in that config since @jorgebucaran had Webpack outputting something similar.

Since that bundle is for the browser, I think it's correct (per this tutorial).

I've heard it said before that Rollup is a great choice for library authors. Vue.js uses it with Buble (instead of Babel).

@jorgebucaran
Copy link
Owner Author

@tunnckoCore We've actually got a smaller bundle, so I'd like to think the current setup is a step in the right direction. @SkaterDad I'd really like to give rollup a try though. Can you help with this?

@tunnckoCore
Copy link

tunnckoCore commented Feb 6, 2017

@jbucaran @SkaterDad I use Rollup + Buble everywhere every day too. It is just amazing combo. But in some cases it is not cool, because Buble adds more bytes as needed - I'm fighting that today and I end up just to not use buble haha and write vanilla es5. In my current case 50-100 bytes is important - because the promo slogan. Here if use Rollup + Buble the case would be the same, because of the app.js#L152-L169 (defining function in loop).

I can PR with Rollup+Buble+UglifyJS setup if you want, but don't know if it worths.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 6, 2017

@tunnckoCore Totally! Can you try to make our dist bundle smaller? 馃槃 馃檹

@SkaterDad
Copy link
Contributor

SkaterDad commented Feb 6, 2017

Sounds like @tunnckoCore is the right guy for the PR. I've only been playing with Rollup since this morning.

I was able to change my config to use buble, though, and it saved ~0.34 KB compared to the Babel w/ ES2015 preset once minified.

The nicest part is it took zero options!

import buble from 'rollup-plugin-buble'
import uglify from 'rollup-plugin-uglify';

export default {
  entry: 'src/app.js',
  plugins: [
    buble(),
    uglify()
  ],
  targets: [
    {
      dest: 'dist/app.buble.js',
      format: 'iife',
      moduleName: 'app',
      sourceMap: true
    }
  ]
};

For the standalone app.js build, here are more exact savings. (based on a clone I pulled down a couple hours ago, so your results may vary)
Webpack -> Babel w/ ES2015 & Babili presets -> Uglify = 3873 chars.
Rollup -> Babel ES2015 preset -> Uglify = 3443 chars.
Rollup -> Buble -> Uglify = 3078 chars.

I'll run the whole index.js through it now.

@jorgebucaran
Copy link
Owner Author

@SkaterDad Are you including html.min.js as well right?

In the future you may want to use https://www.npmjs.com/package/yo-yoify so you only include h or bable/buble for JSX.

@SkaterDad
Copy link
Contributor

The build I'm talking about in my posts is just replicating your library bundle app.min.js.

Using yo-yoify for a client app build is definitely a good idea, but outside the scope of this thread, I think.

@jorgebucaran
Copy link
Owner Author

@SkaterDad Yes, indeed, my bad for losing the thread of the conversation haha.

@tunnckoCore
Copy link

@jbucaran yo-yoify is cool, but is part of the Browserify stack, so I don't see how we can integrate it if we use Rollup/Webpack.

@SkaterDad
Copy link
Contributor

@tunnckoCore I don't think he was saying to use it in the library bundles, but for the end users.

In my own apps, I'm more likely to just use raw h function calls anyway. One less dep to introduce bugs.

@tunnckoCore
Copy link

I know that. But it can't be integrated in our dev flow, because we are using Webpack/Rollup. ;d

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 6, 2017

@tunnckoCore I'm talking about consumers / users using yo-yoify, not us / HyperApp repo.

@jorgebucaran
Copy link
Owner Author

@maraisr Maybe you can look into this? Rollup instead of webpack thing. Specially if it means less or no configuration at all.

@tunnckoCore
Copy link

Yea it means. :) I can start it soon.

@jorgebucaran
Copy link
Owner Author

@SkaterDad How did you get rollup to create multiple bundles? We have to generate one for each file h, html, app and a big one with everything too.

@tunnckoCore
Copy link

I believe there is no support for such thing currently. We will need separate rollup.config.js files. Convention is to have build dir with few rollup configs.

@terkelg
Copy link
Contributor

terkelg commented Feb 6, 2017

I'd vote for Rollup too. I spent a lot of time comparing them a month ago, and Rollup does indeed produce smaller bundles and it's way simpler to configure. In my opinion webpack is a bit overkill for this project, where the purpose is to keep it simple and tiny.

@jorgebucaran
Copy link
Owner Author

@tunnckoCore I'm trying to cram everything in a one liner, so that I can just put everything in package.json.

@SkaterDad
Copy link
Contributor

@jbucaran This issue on Rollup's repo discusses the options: rollup/rollup#703

Simplest thing might be to create a build.js or something, and just programmatically run Rollup.

@jorgebucaran
Copy link
Owner Author

@tunnckoCore How can I call rollup in the cli and specify plugins?

@maraisr
Copy link
Contributor

maraisr commented Feb 6, 2017

Not sure if either of you are aware - but webpack recently had a massive rewrite. So I'd argue todo another comparison. But even then, I'd go webpack over rollup... you don't wanna box yourself in now with a tool that's limited in flexibility.

@jorgebucaran
Copy link
Owner Author

@maraisr Smallest bundle wins.

@maraisr
Copy link
Contributor

maraisr commented Feb 6, 2017

It's great and all.... but what about in the future when we want optional imports, like ssr. Feel like we're gonna spend more time getting roll up todo what we want it too.... oh well. Maybe there's value.... who knows. All I'll have to say is, you want a tool with a solid community, solid Eco system, and something that makes it almost unusable if you wanna try cli. CLI is bad. You'll find the larger a project gets, and the more contributes you get, you'll start to favour readability.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 6, 2017

@maraisr HyperApp should not get larger by principle. It should remain simple. I want to start building stuff with HyperApp, not continue to build HyperApp ad infinitum.

What do you mean by optional imports? We already have optional stuff right? h or html, and so forth.

@maraisr
Copy link
Contributor

maraisr commented Feb 6, 2017

Just use it. You're the author, you ultimately have final say. Let's just agree to disagree?

@maraisr
Copy link
Contributor

maraisr commented Feb 6, 2017

And to answer your other question, Yes, I'll migrate to rollup, and amend this PR. Give me 2 hours, gotta get ready for work.

@tunnckoCore
Copy link

Btw, ES2015-ing also brings more bytes to the final bundle. :D

Because things like that

const defer = (fn, data) => {
  setTimeout(_ => fn(data), 0)
}

a lot smaller would be if it is just defined as function

function defer (fn, data) {
  setTimout(() => {
    fn(data)
  })
}

just think a bit. So arrows should be used only when it make sense not just for the sake of ES6.

@maraisr
Copy link
Contributor

maraisr commented Feb 7, 2017 via email

@jorgebucaran
Copy link
Owner Author

@tunnckoCore I can't repro this. Rollup generates a smaller bundle size for the first example using both arrow functions, what did I miss?

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 7, 2017

Smallest.

const defer = (fn, data) => {
  setTimeout(_ => {
    fn(data)
  }, 0)
}

@tunnckoCore
Copy link

Doesn't make sense. Just think a bit. :) In ES2015 (first case / you "smallest") there would have var and return. Try that

defer.js

// defer1.js
// const defer = (fn, data) => {
//   setTimeout(() => fn(data), 0)
// }

// defer2.js
// function defer () {
//   setTimeout(() => {
//     fn(data)
//   }, 0)
// }

// defer3.js
const defer = (fn, data) => {
  setTimeout(_ => {
    fn(data)
  }, 0)
}

export default defer

and rollup config: buble + uglify plugins

const buble = require('rollup-plugin-buble')
const uglify = require('rollup-plugin-uglify')

module.exports = {
  plugins: [
    buble(),
    uglify({ compress: { warnings: false } })
  ]
}

cli

rollup -c -f iife -n defer -i defer.js -o dist/defer1.js
rollup -c -f iife -n defer -i defer.js -o dist/defer2.js
rollup -c -f iife -n defer -i defer.js -o dist/defer3.js

ls -al dist

output

-rw-r--r-- 1 charlike users  106 Feb  7 09:37 defer1.js
-rw-r--r-- 1 charlike users   95 Feb  7 09:37 defer2.js
-rw-r--r-- 1 charlike users  100 Feb  7 09:38 defer3.js

@lukeed
Copy link

lukeed commented Feb 8, 2017

@jorgebucaran If you're aiming for smallest bundle size (I know you are 馃槈) go with Rollup & Bubl茅. With only quick glances, I see nothing in Hyperapp that inhibits this combo.

Webpack has a future goal of beating Rollup in footprint size, but they have a way to go.
Bubl茅 is always the right choice.

@jorgebucaran
Copy link
Owner Author

jorgebucaran commented Feb 8, 2017

@lukeed The source is now pretty much ES3 + CommonJS, with a few ES5 idioms, probably Array.isArray is the only one, which is okay.

And, yes we ended up going with rollup, which yielded the smallest bundle.

Closing as this information is already out of date.


Repository owner locked and limited conversation to collaborators Feb 8, 2017
@jorgebucaran jorgebucaran removed the docs You read it here first label Aug 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants