MCPcopy Index your code
hub / github.com/choojs/choo

github.com/choojs/choo @v6.13.3 sqlite

repository ↗ · DeepWiki ↗ · release v6.13.3 ↗
60 symbols 82 edges 20 files 0 documented · 0%
README

Choo

:steam_locomotive::train::train::train::train::train:

Fun functional programming

A 4kb framework for creating sturdy frontend applications

API stability

NPM version

Build Status

Test Coverage

Download

Standard

Website | Handbook | Ecosystem | Contributing | Reddit | Chat

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][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 treated as part of the url when routing. Using hashes to delimit routes (e.g. /foo#bar) can be disabled by setting the hash option to false. Regardless, when a hash is found we also check if there's an available anchor on the same page, and will scroll the screen to the position. 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`

Hello ${state.name}

`
})

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

console.log(string)
// => '

Hello Node

'

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][nanocomponent] for documentation on the component class.

```javascript // 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.

Extension points exported contracts — how you extend this code

IChoo (Interface)
(no doc)
index.d.ts
IState (Interface)
(no doc)
index.d.ts

Core symbols most depended-on inside this repo

init
called by 13
test/browser.js
render
called by 8
example/stores/todos.js
filterButton
called by 3
example/components/footer/filter-button.js
todoStore
called by 3
example/stores/todos.js
setState
called by 2
example/components/todos/index.js
setState
called by 2
example/components/footer/index.js
createElement
called by 1
example/components/info.js
toggleAll
called by 1
example/components/todos/index.js

Shape

Function 32
Method 16
Class 10
Interface 2

Languages

TypeScript100%

Modules by API surface

example/stores/todos.js10 symbols
example/components/todos/index.js8 symbols
example/components/todos/todo.js7 symbols
example/components/header.js6 symbols
example/components/footer/index.js6 symbols
test/browser.js4 symbols
index.d.ts4 symbols
example/components/info.js4 symbols
test/node.js3 symbols
index.js2 symbols
example/components/footer/clear-button.js2 symbols
component/cache.js2 symbols

Dependencies from manifests, versioned

@tap-format/spec0.2.0 · 1×
@types/node10.3.1 · 1×
bankai9.0.0-rc6 · 1×
browserify16.2.2 · 1×
bundle-collapser1.2.1 · 1×
choo-devtools2.3.3 · 1×
dependency-check3.1.0 · 1×
disc1.3.3 · 1×
document-ready2.0.1 · 1×
hyperscript2.0.2 · 1×
nanoassert1.1.0 · 1×
nanobus4.2.0 · 1×

For agents

$ claude mcp add choo \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact