Detalhes do pacote

choo

choojs2.7kMIT7.1.0

A 4kb framework for creating sturdy frontend applications

client, frontend, framework, minimal

readme (leia-me)

Choo

:steam_locomotive::train::train::train::train::train:
Fun functional programming
A 4kb framework for creating sturdy frontend applications


The little framework that could. Built with ❤︎ by Yoshua Wuyts and contributors

Table of Contents

Features

  • minimal size: weighing 4kb, Choo is a tiny little framework
  • event based: our performant event system makes writing apps easy
  • small api: with only 6 methods there's not much to learn
  • minimal tooling: built for the cutting edge browserify compiler
  • isomorphic: renders seamlessly in both Node and browsers
  • very cute: choo choo!

Example

var html = require('choo/html')
var devtools = require('choo-devtools')
var choo = require('choo')

var app = choo()
app.use(devtools())
app.use(countStore)
app.route('/', mainView)
app.mount('body')

function mainView (state, emit) {
  return html`
    <body>
      <h1>count is ${state.count}</h1>
      <button onclick=${onclick}>Increment</button>
    </body>
  `

  function onclick () {
    emit('increment', 1)
  }
}

function countStore (state, emitter) {
  state.count = 0
  emitter.on('increment', function (count) {
    state.count += count
    emitter.emit('render')
  })
}

Want to see more examples? Check out the Choo handbook.

Philosophy

We believe programming should be fun and light, not stern and stressful. It's cool to be cute; using serious words without explaining them doesn't make for better results - if anything it scares people off. We don't want to be scary, we want to be nice and fun, and then casually be the best choice around. Real casually.

We believe frameworks should be disposable, and components recyclable. We don't want a web where walled gardens jealously compete with one another. By making the DOM the lowest common denominator, switching from one framework to another becomes frictionless. Choo is modest in its design; we don't believe it will be top of the class forever, so we've made it as easy to toss out as it is to pick up.

We don't believe that bigger is better. Big APIs, large complexities, long files - we see them as omens of impending userland complexity. We want everyone on a team, no matter the size, to fully understand how an application is laid out. And once an application is built, we want it to be small, performant and easy to reason about. All of which makes for easy to debug code, better results and super smiley faces.

Events

At the core of Choo is an event emitter, which is used for both application logic but also to interface with the framework itself. The package we use for this is nanobus.

You can access the emitter through app.use(state, emitter, app), app.route(route, view(state, emit)) or app.emitter. Routes only have access to the emitter.emit method to encourage people to separate business logic from render logic.

The purpose of the emitter is two-fold: it allows wiring up application code together, and splitting it off nicely - but it also allows communicating with the Choo framework itself. All events can be read as constants from state.events. Choo ships with the following events built in:

'DOMContentLoaded'|state.events.DOMCONTENTLOADED

Choo emits this when the DOM is ready. Similar to the DOM's 'DOMContentLoaded' event, except it will be emitted even if the listener is added after the DOM became ready. Uses document-ready under the hood.

'render'|state.events.RENDER

This event should be emitted to re-render the DOM. A common pattern is to update the state object, and then emit the 'render' event straight after. Note that 'render' will only have an effect once the DOMContentLoaded event has been fired.

'navigate'|state.events.NAVIGATE

Choo emits this event whenever routes change. This is triggered by either 'pushState', 'replaceState' or 'popState'.

'pushState'|state.events.PUSHSTATE

This event should be emitted to navigate to a new route. The new route is added to the browser's history stack, and will emit 'navigate' and 'render'. Similar to history.pushState.

'replaceState'|state.events.REPLACESTATE

This event should be emitted to navigate to a new route. The new route replaces the current entry in the browser's history stack, and will emit 'navigate' and 'render'. Similar to history.replaceState.

'popState'|state.events.POPSTATE

This event is emitted when the user hits the 'back' button in their browser. The new route will be a previous entry in the browser's history stack, and immediately afterward the'navigate' and 'render'events will be emitted. Similar to history.popState. (Note that emit('popState') will not cause a popState action - use history.go(-1) for that - this is different from the behaviour of pushState and replaceState!)

'DOMTitleChange'|state.events.DOMTITLECHANGE

This event should be emitted whenever the document.title needs to be updated. It will set both document.title and state.title. This value can be used when server rendering to accurately include a <title> tag in the header. This is derived from the DOMTitleChanged event.

State

Choo comes with a shared state object. This object can be mutated freely, and is passed into the view functions whenever 'render' is emitted. The state object comes with a few properties set.

When initializing the application, window.initialState is used to provision the initial state. This is especially useful when combined with server rendering. See server rendering for more details.

state.events

A mapping of Choo's built in events. It's recommended to extend this object with your application's events. By defining your event names once and setting them on state.events, it reduces the chance of typos, generally autocompletes better, makes refactoring easier and compresses better.

state.params

The current params taken from the route. E.g. /foo/:bar becomes available as state.params.bar If a wildcard route is used (/foo/*) it's available as state.params.wildcard.

state.query

An object containing the current queryString. /foo?bin=baz becomes { bin: 'baz' }.

state.href

An object containing the current href. /foo?bin=baz becomes /foo.

state.route

The current name of the route used in the router (e.g. /foo/:bar).

state.title

The current page title. Can be set using the DOMTitleChange event.

state.components

An object recommended to use for local component state.

state.cache(Component, id, [...args])

Generic class cache. Will lookup Component instance by id and create one if not found. Useful for working with stateful components.

Routing

Choo is an application level framework. This means that it takes care of everything related to routing and pathnames for you.

Params

Params can be registered by prepending the route name with :routename, e.g. /foo/:bar/:baz. The value of the param will be saved on state.params (e.g. state.params.bar). Wildcard routes can be registered with *, e.g. /foo/*. The value of the wildcard will be saved under state.params.wildcard.

Default routes

Sometimes a route doesn't match, and you want to display a page to handle it. You can do this by declaring app.route('*', handler) to handle all routes that didn't match anything else.

Querystrings

Querystrings (e.g. ?foo=bar) are ignored when matching routes. An object containing the key-value mappings exists as state.query.

Hash routing

By default, hashes are ignored when routing. When enabling hash routing (choo({ hash: true })) hashes will be treated as part of the url, converting /foo#bar to /foo/bar. This is useful if the application is not mounted at the website root. Unless hash routing is enabled, if a hash is found we check if there's an anchor on the same page, and will scroll the element into view. Using both hashes in URLs and anchor links on the page is generally not recommended.

Following links

By default all clicks on <a> tags are handled by the router through the nanohref module. This can be disabled application-wide by passing { href: false } to the application constructor. The event is not handled under the following conditions:

  • the click event had .preventDefault() called on it
  • the link has a target="_blank" attribute with rel="noopener noreferrer"
  • a modifier key is enabled (e.g. ctrl, alt, shift or meta)
  • the link's href starts with protocol handler such as mailto: or dat:
  • the link points to a different host
  • the link has a download attribute

:warn: Note that we only handle target=_blank if they also have rel="noopener noreferrer" on them. This is needed to properly sandbox web pages.

Navigating programmatically

To navigate routes you can emit 'pushState', 'popState' or 'replaceState'. See #events for more details about these events.

Server Rendering

Choo was built with Node in mind. To render on the server call .toString(route, [state]) on your choo instance.

var html = require('choo/html')
var choo = require('choo')

var app = choo()
app.route('/', function (state, emit) {
  return html`<div>Hello ${state.name}</div>`
})

var state = { name: 'Node' }
var string = app.toString('/', state)

console.log(string)
// => '<div>Hello Node</div>'

When starting an application in the browser, it's recommended to provide the same state object available as window.initialState. When the application is started, it'll be used to initialize the application state. The process of server rendering, and providing an initial state on the client to create the exact same document is also known as "rehydration".

For security purposes, after window.initialState is used it is deleted from the window object.

<html>
  <head>
    <script>window.initialState = { initial: 'state' }</script>
  </head>
  <body>
  </body>
</html>

Components

From time to time there will arise a need to have an element in an application hold a self-contained state or to not rerender when the application does. This is common when using 3rd party libraries to e.g. display an interactive map or a graph and you rely on this 3rd party library to handle modifications to the DOM. Components come baked in to Choo for these kinds of situations. See nanocomponent for documentation on the component class.

// map.js
var html = require('choo/html')
var mapboxgl = require('mapbox-gl')
var Component = require('choo/component')

module.exports = class Map extends Component {
  constructor (id, state, emit) {
    super(id)
    this.local = state.components[id] = {}
  }

  load (element) {
    this.map = new mapboxgl.Map({
      container: element,
      center: this.local.center
    })
  }

  update (center) {
    if (center.join() !== this.local.center.join()) {
      this.map.setCenter(center)
    }
    return false
  }

  createElement (center) {
    this.local.center = center
    return html`<div></div>`
  }
}
// index.js
var choo = require('choo')
var html = require('choo/html')
var Map = require('./map.js')

var app = choo()
app.route('/', mainView)
app.mount('body')

function mainView (state, emit) {
  return html`
    <body>
      <button onclick=${onclick}>Where am i?</button>
      ${state.cache(Map, 'my-map').render(state.center)}
    </body>
  `

  function onclick () {
    emit('locate')
  }
}

app.use(function (state, emitter) {
  state.center = [18.0704503, 59.3244897]
  emitter.on('locate', function () {
    window.navigator.geolocation.getCurrentPosition(function (position) {
      state.center = [position.coords.longitude, position.coords.latitude]
      emitter.emit('render')
    })
  })
})

Caching components

When working with stateful components, one will need to keep track of component instances – state.cache does just that. The component cache is a function which takes a component class and a unique id (string) as its first two arguments. Any following arguments will be forwarded to the component constructor together with state and emit.

The default class cache is an LRU cache (using nanolru), meaning it will only hold on to a fixed amount of class instances (100 by default) before starting to evict the least-recently-used instances. This behavior can be overriden with options.

Optimizations

Choo is reasonably fast out of the box. But sometimes you might hit a scenario where a particular part of the UI slows down the application, and you want to speed it up. Here are some optimizations that are possible.

Caching DOM elements

Sometimes we want to tell the algorithm to not evaluate certain nodes (and its children). This can be because we're sure they haven't changed, or perhaps because another piece of code is managing that part of the DOM tree. To achieve this nanomorph evaluates the .isSameNode() method on nodes to determine if they should be updated or not.

var el = html`<div>node</div>`

// tell nanomorph to not compare the DOM tree if they're both divs
el.isSameNode = function (target) {
  return (target && target.nodeName && target.nodeName === 'DIV')
}

Reordering lists

It's common to work with lists of elements on the DOM. Adding, removing or reordering elements in a list can be rather expensive. To optimize this you can add an id attribute to a DOM node. When reordering nodes it will compare nodes with the same ID against each other, resulting in far fewer re-renders. This is especially potent when coupled with DOM node caching.

var el = html`
  <section>
    <div id="first">hello</div>
    <div id="second">world</div>
  </section>
`

Pruning dependencies

We use the require('assert') module from Node core to provide helpful error messages in development. In production you probably want to strip this using unassertify.

To convert inlined HTML to valid DOM nodes we use require('nanohtml'). This has overhead during runtime, so for production environments we should unwrap this using the nanohtml transform.

Setting up browserify transforms can sometimes be a bit of hassle; to make this more convenient we recommend using bankai build to build your assets for production.

FAQ

Why is it called Choo?

Because I thought it sounded cute. All these programs talk about being "performant", "rigid", "robust" - I like programming to be light, fun and non-scary. Choo embraces that.

Also imagine telling some business people you chose to rewrite something critical for serious bizcorp using a train themed framework. :steam_locomotive::train::train::train:

Is it called Choo, Choo.js or...?

It's called "Choo", though we're fine if you call it "Choo-choo" or "Chugga-chugga-choo-choo" too. The only time "choo.js" is tolerated is if / when you shimmy like you're a locomotive.

Does Choo use a virtual-dom?

Choo uses nanomorph, which diffs real DOM nodes instead of virtual nodes. It turns out that browsers are actually ridiculously good at dealing with DOM nodes, and it has the added benefit of working with any library that produces valid DOM nodes. So to put a long answer short: we're using something even better.

How can I support older browsers?

Template strings aren't supported in all browsers, and parsing them creates significant overhead. To optimize we recommend running browserify with nanohtml as a global transform or using bankai directly.

$ browserify -g nanohtml

Is choo production ready?

Sure.

API

This section provides documentation on how each function in Choo works. It's intended to be a technical reference. If you're interested in learning choo for the first time, consider reading through the handbook first :sparkles:

app = choo([opts])

Initialize a new choo instance. opts can also contain the following values:

  • opts.history: default: true. Listen for url changes through the history API.
  • opts.href: default: true. Handle all relative <a href="<location>"></a> clicks and call emit('render')
  • opts.cache: default: undefined. Override default class cache used by state.cache. Can be a a number (maximum number of instances in cache, default 100) or an object with a nanolru-compatible API.
  • opts.hash: default: false. Treat hashes in URLs as part of the pathname, transforming /foo#bar to /foo/bar. This is useful if the application is not mounted at the website root.

app.use(callback(state, emitter, app))

Call a function and pass it a state, emitter and app. emitter is an instance of nanobus. You can listen to messages by calling emitter.on() and emit messages by calling emitter.emit(). app is the same Choo instance. Callbacks passed to app.use() are commonly referred to as 'stores'.

If the callback has a .storeName property on it, it will be used to identify the callback during tracing.

See #events for an overview of all events.

app.route(routeName, handler(state, emit))

Register a route on the router. The handler function is passed app.state and app.emitter.emit as arguments. Uses nanorouter under the hood.

See #routing for an overview of how to use routing efficiently.

app.mount(selector)

Start the application and mount it on the given querySelector, the given selector can be a String or a DOM element.

In the browser, this will replace the selector provided with the tree returned from app.start(). If you want to add the app as a child to an element, use app.start() to obtain the tree and manually append it.

On the server, this will save the selector on the app instance. When doing server side rendering, you can then check the app.selector property to see where the render result should be inserted.

Returns this, so you can easily export the application for server side rendering:

module.exports = app.mount('body')

tree = app.start()

Start the application. Returns a tree of DOM nodes that can be mounted using document.body.appendChild().

app.toString(location, [state])

Render the application to a string. Useful for rendering on the server.

choo/html

Create DOM nodes from template string literals. Exposes nanohtml. Can be optimized using nanohtml.

choo/html/raw

Exposes nanohtml/raw helper for rendering raw HTML content.

Installation

$ npm install choo

See Also

  • bankai - streaming asset compiler
  • stack.gl - open software ecosystem for WebGL
  • yo-yo - tiny library for modular UI
  • tachyons - functional CSS for humans
  • sheetify - modular CSS bundler for browserify

Support

Creating a quality framework takes a lot of time. Unlike others frameworks, Choo is completely independently funded. We fight for our users. This does mean however that we also have to spend time working contracts to pay the bills. This is where you can help: by chipping in you can ensure more time is spent improving Choo rather than dealing with distractions.

Sponsors

Become a sponsor and help ensure the development of independent quality software. You can help us keep the lights on, bellies full and work days sharp and focused on improving the state of the web. Become a sponsor

Backers

Become a backer, and buy us a coffee (or perhaps lunch?) every month or so. Become a backer

License

MIT

changelog (log de mudanças)

7.0.0 All the little things

It has been quite a while since the last major version of Choo. Since it's release, choo@6 has recieved a bunch of features and patches, but some changes has been put off because they'd have an affect on the API in a way that could be breaking for some users. With this release we've merged all these minor, but breaking changes all at once.

The way in which Choo handles hashes in the URL has proved confusing for both newcomers and seasoned Choo users. In a prior patch the hash option was added which could be used to disable hash routing. With this release we're changing it to be disabled by default.

Rendering pages server side is an excellent way to increase page performance and Choo supports it out of the box. Choo also allows for rehydrating the client with what state was used when rendering on the server side. To ensure that the application state is identicial regardless if hydrated or not, the internals for routing has been changes so that href, query, param and route are all available on application state at the time when stores are initialized.

In prior versions, when rendering on the server, the state that was provided to toString would be merged onto the application state. Meaning consecutive calls to toString would accumulate on the application state and persist in-between render. With this change, application state is no longer mutated during server side render. This will affects those of you doing server side rendering and then plucking out properties from the application state. You can now be assured that only the state that is provided to toString is modified during rendering.

var html = app.toString('/', state)
- var title = app.state.title
+ var title = state.title

And lastly, we've updated depndencies and even dropped a dependency which is no longer required by modern browsers. The dependency xtend was dropped in favour of Object.assign. This change has also been propagated throughout the Choo universe i.e. choo-devtools and choo-service-worker etc. If you are supporting legacy browsers (IE11 and bellow), we recommend the polyfill service https://polyfill.io which will detect legacy browsers and load the appropiate polyfills.

That's all for now, keep being awesome!

6.0.0 Same as it ever was

In the past few months we've been able to use choo@5 a bunch, and people seem to like it a lot! In this patch all we're doing is taking choo's existing API, and polishing the internals. In theory this means breaking changes, but in practice it will mostly mean people need to update their dependencies, and things will work great.

Choo v6 includes the upgrades to bel@5 and nanomorph@5. This means up to 20x faster server rendering, and up to 10x improved browser rendering speeds. We've also fixed DOM node caching, and introduced sibling node reordering.

This release also includes an overhauled timing API. We're now capturing more events, and are leveraging this in tools like choo-log to provide consistent feedback on performance.

Choo is now also completely prototype based. It not only means faster execution times, and more maintainable codebase - but you can also override methods if you ever need to.

We've also tweaked the event system a little. All built-in event names are now available as constants under app.state.events. And we've introduced three new events: 'navigate' will trigger whenever a route changes, 'replaceState' can be called to redirect routes, and popState which is emitted when the back button in the browser is pressed.

To top things off, we've reintroduced querystring parsing. An object containing the current represenation of the search query (e.g. ?foo=bar) can be found under state.query. We used to do something similar in choo v4 and below, and we're happy to reintroduce it in this release!

And that's about it - we've upgraded a whole slew of deps, and removed a few we didn't quite use. Overall we're quite proud of the new codebase, and filled with joy we didn't have to make any changes to the API - additions only.

Thanks heaps for reading this far, we hope you enjoy this release as much as we did building it. Happy coding! -Team Choo :sparkles:

5.1.0 Timing API support

In order to improve, we must measure first. Specifically when it comes to framerate there are very specific numbers we can rely on: ~16ms for any given frame to achieve 60fps. That's why in 5.1.0 we're adding support for the window.Performance API.

We hope that by adding support for timers, people building applications on choo will become more aware of their application's performance and learn how and when to optimize. Hopefully this will help in making applications accessible to all sorts of devices, and not just the latest and greatest.

Timing support will be enabled by default, and can be toggled off by passing { timing: false } to the var app = choo() constructor.

Timing calls will not run in browsers that don't support it out of the box. For unsupported browser's there's a polyfill available at nolanlawson/marky. The timing marks are choo:renderStart, choo:renderEnd. The resulting diff is stored as choo:render.

We hope you'll enjoy this release; thanks heaps for using choo!

changes

  • added out of the box support for performance timings (window.performance)
  • updated nanobus to 3.0.0; '*' events now run after named events

5.0.0 Welp Welp Welp

So it turns out Choo could be radically simplified. We're now comfortably sitting at ~4kb, have removed a whole bunch of words from the API and should be a whole lot faster. We've written about it before; if you're interested we recommend reading through that post.

We're now using an event emitter, mutable state and explicit re-renders. Some people might frown at first at the words "mutable state", but if you think it through the mental model doesn't change. "State" has always been a concept of an object that changes over time; we then render the DOM as a snapshot of that state.

What we've done is change the way we mutate that state - we no longer generate a ton of expensive intermediate objects to mutate the state, but instead mutate the state directly. In turn we've also removed the magic re-rendering and made it explicit. This enables people to create tight render loops that can even be used in GC constrained environments like games or music production. We think this change was well worth it, and will make a lot of sense going forward.

People might also wonder why we've moved away from flux/elm and are using an event emitter now. It turns out that the previous architecture had a lot of confusing words that made it harder to learn than it should. It was also not possible to react to changes; the thing that changed always had to specify what needed to respond to it. By using event emitters we've changed this, which will make relations in the application more expressive. All in all, it turned out that all we needed for this was a simple event emitter - we think this was well worth the change and breaking away from what we were previously doing.

Pretty much everything about the API changed in this version. There's literally nothing left to remove from the API tho so this is probably the last time we get to break anything in a significant way.

changes

  • :exclamation: state is now mutable and renders are triggered through .emit('render').
  • :exclamation: we've replaced .use(), .model() and the rest of the choo architecture with a reworked .use() method. It's called once on boot, and exposes a mutable reference to state and an event emitter that's compatible with Node's require('events').EventEmitter
  • :exclamation: the .router() method has been replaced with .route(), replacing the nested array API. This should be easier to remember and more performant.
  • :exclamation: we've replaced morphdom/yo-yo with nanomorph. The two algorithms are very comparable. The differences are that the new algorithm is smaller and the value of input fields on re-rendering will be whatever the value="" attribute is.
  • :exclamation: choo/mount is now available as app.mount() and calls app.start() internally now

4.0.0 The routing patch

This patch changes the way we handle routes. It introduces query string support (!), and changes the router to use a lisp-like syntax. It also inverts the argument order of effects and reducers to be more intuitive. We also managed to sneak in some performance upgrades :sparkles: - We hope you enjoy it!

changes

  • :exclamation: slim down server side rendering API | issue | pull-request
  • :exclamation: update router API to be lisp-like
  • :exclamation: swap state and data argument order | issue
  • :exclamation: remove choo/http. Use xhr instead | pull-request
  • update router to use memoization | issue | pull-request
  • support inline anchor links | issue
  • allow bypassing of link clicks in sheet-router | issue | pull-request
  • update router API to handle hashes by default
  • update router to provide out of the box support for Electron
  • update location state to expose search parameters (query strings) | issue

3.3.0

Yay, plugins now support wrappers which is a segway onto HMR, time travel and other cool plugins. These changes have come through in barracks v8.3.0 and a lil fix in v8.3.1. This is a lil patch before 4.0.0 comes through, but should be super valuable. Wooh!

changes

  • updated barracks to v8.3.1

3.2.0

Wooh, plugins are a first class citizen now thanks to the .use() API. It's a multiplexed version of the old app = choo(hooks). It should enable attaching multiple hooks onto the same API, which is useful to create re-usable extensions to choo. They should be used with care though, and be as generic as possible, but the docs should provide enough backdrop for that. Anyway, have fun with plugins! :tada:

changes

  • added app.use()

3.1.0

And another patch down. This time around it's mostly maintenance and a bit of perf:

  • The addition of the nanoraf dependency prevents bursts of DOM updates thrashing application performance, quite possibly making choo amongst the fastest frameworks out there.
  • We now ship standalone UMD bundles on each release, available through https://unpkg.com/choo. The goal of this is to support sites like codepen and the like; this should not be used for production.

3.0.0

Woooh, happy third birthday choo - thanks dad. You're all grown up now; look at how far you've come in the last month. You've grown... tinier? But yet you do more? I love you choo - shut up dad.

Notable changes

Who's the tiniest of them all?

choo is now 5kb optimized! That's 2kb less compared to v2. Woah, how? We now support yo-yoify which optimizes those lil template tags to document.createElement() calls. So not only is it smaller, creating elements now has no overhead. Pretty nifty eh? Mad shoutout to Shama for building this!

Captain Hook(s)

V3 introduces hooks - powerful functions that are called at certain points in the refresh cycle. Unlike functions in models these functions have unfiltered access to all properties, call stacks and more. They're super useful when building error handling, logging or persisting for stuff like hot reloading. I quite like them, and I'm def keen to see what uses people will come up with!

Effect Composition :train::train::train::train:

effects are now composable by calling a done(err, res) callback when they're done executing. This means that multiple namespaced effects can be chained together to form some higher level behavior.

Think of cases like "logout" - multiple models must be cleared, perhaps tokens invalidated on the server, all in a certain order. This requires multiple models to work in tandem. - And now that's possible! :sparkles:

Pathfinders guide

We've started work on the choo handbook - a lil manual to help you get started, not only with choo, but with web development in general. It's super modest still, only containing a single choo tutorial, but we'll be expanding this over the coming months. If you want to contribute some docs, there's a whole section of ideas on stuff that might be neat to write. Any lil bits are welcome! Shout out to Tim for making this happen :tada:

The Cycle of Life

views have gone through a bit of a change - they're now required using require('choo/html') so they can be factored out of a project into standalone bel components at any time. But additionally these components have gained super powers through the adition of onload and onunload hooks. Components can now react to being mounted or not, which makes them ideal to implement standalone widgets. This behavior uses html5 MutationObserver under the hood, so it will work anywhere with a DOM! Again, this was all Shama's hard work.

Test coverage

choo has gained a beaut blanket of tests, courtesy of Todd and Ben. We've got server, browser and pretty-much-all-browsers-known-to-mankind style testing which should give us a pretty good idea if stuff breaks. Neat!

Core dump

Internally we've moved the core of choo into a separate package - barracks. choo is now mere glue code around barracks, yo-yo and sheet-router. This is good news for folks who like choo, but don't agree with all decisions. Go forth and build your own lil framework!

Changelog

  • move choo.view out to require('choo/html') #71 | pr #103
  • streamline view API #35 | pr #111
  • higher order functions #34 | pr #104
  • create lifecycle hooks #1 | feature addition in dependency covered by semver
  • implement state hooks #15 | pr #104
  • add yo-yoify #3 | pr #110
  • rename "app" namespace #82 | pr #111
  • enable browser testing | pr #86
  • propagating actions creates infinite loop #114 | pr #104
  • state is now immutable in reducers and effects

Thanks

Huge thanks to everyone who's collaborated on this, provided feedback or even mentioned it anywhere. It's been a hella lot of people, but seriously, you're the best :steam_locomotive::train::train::train::train::train:


2.3.1

  • 76 - fix router arguments

2.3.0

  • 55 - load subscriptions once DOM is ready
  • heaps of documentation fixes; looks like choo is taking off 🐨

2.2.2

  • 53 - fix assert call for subscriptions
  • 52 - fix naming rootId

2.0.0

breaking changes

  • namespaces are now enforced more strictly
  • models now only accept a single argument
  • the namespace key was introduced inside of models (was prior the leading string in models)
  • namespaced models can now only operate within themselves

1.0.0

  • first version of choo