diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index fab411ae4..000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-name: CI
-on: push
-jobs:
- test:
- env:
- NODE_ENV: development
- runs-on: ubuntu-latest
- strategy:
- matrix:
- node-version: [14.x]
- steps:
- - name: Checkout
- uses: actions/checkout@v1
- - name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v1
- with:
- node-version: ${{ matrix.node-version }}
- - name: Test
- run: |
- npm install -g codecov
- npm install
- npm test
- codecov
diff --git a/.gitignore b/.gitignore
index 9ee768669..5fe10f5e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,19 @@
-*.html
-*.map
-*.br
-*.gz
+# IDE
+.idea/
+.vscode/
+# Logs
+*.log*
+yarn.lock
package-lock.json
+
+# Misc
+examples
+notes.md
node_modules
coverage
-private
+*.xml
+# Dist
+*.gz
+dist/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..d45dd92d1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,21 @@
+language: node_js
+node_js:
+ - "6"
+ - "8"
+ - "9"
+
+env:
+ - NODE_ENV=development
+
+before_install:
+ - npm i -g codecov
+
+install:
+ - npm install
+
+script:
+ - npm test
+ - codecov
+
+notifications:
+ email: false
diff --git a/LICENSE.md b/LICENSE.md
index 6ba7a0fbb..6a67a44a3 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,7 +1,19 @@
-Copyright © Jorge Bucaran <{state.count}
+
+
+ ```jsx for best syntax highlighting.
+
+## Tests
+
+* We are currently using [Babel](https://babeljs.io) and [Jest](http://facebook.github.io/jest) for tests.
+* Feel free to create a new `test/*.test.js` file if none of the existing test files suits your test case.
+* Tests usually start by creating a small application and using a feature, then check if `document.body.innerHTML` matches some string. The app() call is async, so we often use [`oncreate`](lifecycle-events.md#oncreate) or [`onupdate`](lifecycle-events.md#onupdate) events to detect when the view has been rendered.
+
+## Humans
+
+Our open source community strives to be nice, welcoming and professional. Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by contacting us at [hyperappjs@gmail.com](hyperappjs@gmail.com).
diff --git a/docs/README.md b/docs/README.md
new file mode 100755
index 000000000..76a3defff
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,17 @@
+# Documentation
+
+* [Introduction](introduction/README.md)
+ * [Hello World](introduction/hello-world.md)
+ * [Installation](introduction/installation.md)
+* [Concepts](concepts/README.md)
+ * [Virtual Nodes](concepts/vnodes.md)
+ * [Keys](concepts/keys.md)
+ * [Components](concepts/components.md)
+ * [Lifecycle Events](concepts/lifecycle-events.md)
+ * [Sanitation](concepts/sanitation.md)
+ * [Hydration](concepts/hydration.md)
+* [Tutorials](tutorials/README.md)
+ * [TweetBox](tutorials/tweetbox.md)
+ * [Gif Search](tutorials/gif-search.md)
+ * [Countdown Timer](tutorials/countdown-timer.md)
+* [Contributing](CONTRIBUTING.md)
diff --git a/docs/api/app.md b/docs/api/app.md
deleted file mode 100644
index 7fb1ba15d..000000000
--- a/docs/api/app.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# `app()`
-
-Initializes and mounts a Hyperapp application.
-
-```elm
-app : ({ Init, View, Node, Subscriptions?, Dispatch? }) -> DispatchFn
-```
-
-| Prop | Type | Required? |
-| -------------------------------- | --------------------------------------------------------------------------- | -------------------------------- |
-| [init:](#init) |
| No |
-| [view:](#view) | [View](../architecture/views.md) | No |
-| [node:](#node) | DOM element | **Yes when `view:` is present.** |
-| [subscriptions:](#subscriptions) | Function | No |
-| [dispatch:](#dispatch) | [Dispatch Initializer](../architecture/dispatch.md#dispatch-initializer) | No |
-
-| Return Value | Type |
-| --------------------------------------- | -------- |
-| [dispatch](../architecture/dispatch.md) | Function |
-
-```js
-import { app, h, text } from "hyperapp"
-
-app({
- init: { message: "Hello World!" },
- view: (state) => h("p", {}, text(state.message)),
- node: document.getElementById("app"),
-})
-```
-
-## `init:`
-
-_(default value: `{}`)_
-
-Initializes the app by either setting the initial value of the [state](../architecture/state.md) or taking an [action](../architecture/actions.md). It takes place before the first view render and subscriptions registration.
-
-### Forms of `init:`
-
-- `init: state`
-
- Sets the initial state directly.
-
- ```js
- app({
- init: { counter: 0 },
- // ...
- })
- ```
-
-- `init: [state, ...effects]`
-
- Sets the initial state and then runs the given list of [effects](../architecture/effects.md).
-
- ```js
- app({
- init: [
- { loading: true },
- log("Loading..."),
- load("myUrl?init", DoneAction),
- ],
- // ...
- })
- ```
-
-- `init: Action`
-
- Runs the given [Action](../architecture/actions.md).
-
- This form is useful when the action can be reused later. The state passed to the action in this case is `undefined`.
-
- ```js
- const Reset = (_state) => ({ counter: 0 })
-
- app({
- init: Reset,
- // ...
- })
- ```
-
-- `init: [Action, payload]`
-
- Runs the given [Action](../architecture/actions.md) with a payload.
-
- ```js
- const SetCounter = (_state, n) => ({ counter: n })
-
- app({
- init: [SetCounter, 10],
- // ...
- })
- ```
-
-## `view:`
-
-The [top-level view](../architecture/views.md#top-level-view) that represents the app as a whole. There can only be one top-level view in your app. Hyperapp uses this to map your state to your UI for rendering the app. Every time the [state](../architecture/state.md) of the application changes, this function will be called to render the UI based on the new state, using the logic you've defined inside of it.
-
-```js
-app({
- // ...
- view: (state) => h("main", {}, [
- outworld(state),
- netherrealm(state),
- ]),
-})
-```
-
-
-
-## `node:`
-
-The DOM element to render the virtual DOM over (the **mount node**). The given element is replaced by a Hyperapp application. This process is called **mounting**. It's common to define an intentionally empty element in your HTML which has an ID that your app can use for mounting. If the mount node had content within it then Hyperapp will attempt to [recycle](../architecture/views.md#recycling) that content.
-
-```html
-
-```
-
-```js
-app({
- // ...
- node: document.getElementById("app"),
-})
-```
-
-## `subscriptions:`
-
-A function that returns an array of [subscriptions](../architecture/subscriptions.md) for a given state. Every time the [state](../architecture/state.md) of the application changes, this function will be called to determine the current subscriptions.
-
-If a subscription entry is falsy then the subscription that was at that spot, if any, will be considered unsubscribed from and will be cleaned up.
-
-If `subscriptions:` is omitted the app has no subscriptions. It behaves the same as if you were using: `subscriptions: (state) => []`
-
-```js
-import { onKey } from "./subs"
-
-app({
- // ...
- subscriptions: (state) => [
- onKey("w", MoveForward),
- onKey("a", MoveBackward),
- onKey("s", StrafeLeft),
- onKey("d", StrafeRight),
- state.playingDOOM1993 || onKey(" ", Jump),
- ],
-})
-```
-
-
-
-## `dispatch:`
-
-A [dispatch initializer](../architecture/dispatch.md#dispatch-initializer) that can create a [custom dispatch function](../architecture/dispatch.md#custom-dispatching) to use instead of the default dispatch. Allows tapping into dispatches for debugging, testing, telemetry etc.
-
-## Return Value
-
-`app()` returns the [dispatch](../architecture/dispatch.md) function your app uses. This can be handy if you want to control your app externally, ie where only a subsection of your app is implemented with Hyperapp.
-
-Calling the dispatch function with no arguments frees the app's resources and runs every active subscription's cleanup function.
-
-## Other Considerations
-
-- You can embed your Hyperapp application within another already existing Hyperapp application or an app that was built with some other framework.
-
-- Multiple Hyperapp applications can coexist on the page simultaneously. They each have their own state and behave independently relative to each other. They can communicate with each other using subscriptions and effects (i.e. using events).
diff --git a/docs/api/h.md b/docs/api/h.md
deleted file mode 100644
index c67077081..000000000
--- a/docs/api/h.md
+++ /dev/null
@@ -1,227 +0,0 @@
-
-
-**_Definition:_**
-
-> A function that creates [virtual DOM nodes (VNodes)](../architecture/views.md#virtual-dom) which are used for defining [views](../architecture/views.md).
-
-**_Import & Usage:_**
-
-```js
-import { h } from "hyperapp"
-
-// ...
-
-h(tag, props, children)
-```
-
-**_Signature & Parameters:_**
-
-```elm
-h : (String, Object, VNode? | [...VNodes]?) -> VNode
-```
-
-| Parameters | Type | Required? |
-| --------------------- | ------------------------ | --------- |
-| [tag](#tag) | String | yes :100: |
-| [props](#props) | Object | yes :100: |
-| [children](#children) | VNode or array of VNodes | no |
-
-| Return Value | Type |
-| ---------------------------------------------------- | ----- |
-| [virtual node](../architecture/views.md#virtual-dom) | VNode |
-
-`h()` effectively represents the page elements used in your app. Because it's just JavaScript we can easily render whichever elements we see fit in a dynamical manner.
-
-```js
-const hobbit = (wearingElvenCloak) =>
- h("div", {}, [
- !wearingElvenCloak && h("p", {}, text("Frodo")),
- ])
-```
-
-
-
----
-
-## Parameters
-
-### `tag`
-
-Name of the node. For example, `div`, `h1`, `button`, etc. Essentially any [HTML element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) or [SVG element](https://developer.mozilla.org/en-US/docs/Web/SVG/Element) or [custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements).
-
-### `props`
-
-HTML or SVG properties ("props") for the DOM element are defined using an object where the keys are the property names and the values are the corresponding property values.
-
-```js
-h("input", {
- type: "checkbox",
- id: "picard",
- checked: state.engaging,
-})
-```
-
-
-
-Hyphenated props will need to be quoted in order to use them. The quotes are necessary to abide by JavaScript syntax restrictions.
-
-```js
-h("q", { "data-zoq-fot-pik": "Frungy" }, text("The Sport of Kings!"))
-```
-
-
-
-Certain properties are treated in a special way by Hyperapp.
-
-#### `class:`
-
-The [classes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) to use with the VNode. The `class` prop can be given in various formats:
-
-- As a string representing a class name. Because of the way Hyperapp internally processes class strings they're allowed to have a space-separated list of different class names.
-
- ```js
- h("div", { class: "muggle-studies" })
- ```
-
-
-
-- As an object where the keys are the names of the classes while the values are booleans for toggling the classes.
-
- ```js
- h("div", { class: { arithmancy: true, "study-of-ancient-runes": true } })
- ```
-
-
-
-- As an array that contains any combination of the various formats including this one.
-
- ```js
- h("div", { class: ["magical theory", "xylomancy"] })
- ```
-
-
-
- This means the array format is recursive.
-
- ```js
- h("input", {
- type: "range",
- class: [
- { dragonzord: state.green && !state.white },
- "mastodon",
- state.pink && "pterodactyl",
- [
- { triceratops: state.blue },
- "sabretooth-tiger",
- state.red && "tyrannosaurus",
- ],
- ],
- })
- ```
-
-
-
-#### `style:`
-
-The [inline CSS styles](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/style) to use with the VNode. The `style` prop can be an object of [CSS properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) where the keys are the CSS property names and the values are the corresponding CSS property values. Hyphenated CSS property names can either be in camelCase or quoted to abide by JavaScript syntax restrictions.
-
-```js
-h(
- "span",
- {
- style: {
- backgroundColor: "white",
- color: "blue",
- display: "inline-block",
- "font-weight": "bold",
- },
- },
- text("+\\")
-)
-```
-
-
-
-#### `key:`
-
-A unique string per VNode that helps Hyperapp track if VNodes are changed, added, or removed in situations where it's unable to do so, such as in arrays.
-
-```js
-const pokedex = (pokemon) =>
- h(
- "ul",
- {},
- pokemon.map((p) => h("li", { key: p.id }, text(p.name)))
- )
-```
-
-
-
-#### Event Listeners
-
-Props that represent event listeners, such as [`onclick`](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event), [`onchange`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event), [`oninput`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event), etc. are where you would assign [actions](../architecture/actions.md) to VNodes.
-
-Synthetic events can be added in the same way as long as their name starts with "on", so an event created with
-```js
-const buildEvent = new Event("build")
-```
-can be used like this:
-```js
-h("button", { onbuild: BuildAction }, text("Click Me"))
-```
-
-### `children`
-
-The children of the VNode are other VNodes which are directly nested within it.
-
-`children` can either be given as a single child VNode:
-
-```js
-h("q", {}, text("There is no spoon."))
-```
-
-
-
-or as an array of child VNodes:
-
-```js
-h("q", {}, [
- text("I know Kung Fu."),
- h("em", {}, text("Show me.")),
-])
-```
-
-
-
----
-
-## Other Considerations
-
-### JSX Support
-
-Hyperapp doesn't support [JSX](https://reactjs.org/docs/introducing-jsx.html) out-of-the-box. That said you can use this custom JSX function to be able to use it.
-
-```js
-import { h, text } from "hyperapp"
-
-const jsxify = (h) => (type, props, ...children) =>
- typeof type === "function"
- ? type(props, children)
- : h(
- type,
- props || {},
- [].concat(...children).map((x) =>
- typeof x === "string" || typeof x === "number" ? text(x) : x
- )
- )
-
-const jsx = jsxify(h) /** @jsx jsx */
-```
diff --git a/docs/api/memo.md b/docs/api/memo.md
deleted file mode 100644
index 50db6027c..000000000
--- a/docs/api/memo.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# `memo()`
-
-**_Definition:_**
-
-> A wrapper function to cache your [views](../architecture/views.md) based on properties you pass into them.
-
-**_Import & Usage:_**
-
-```js
-import { memo } from "hyperapp"
-
-// ...
-
-memo(view, props)
-```
-
-**_Signature & Parameters:_**
-
-```elm
-memo : (View, IndexableData) -> VNode
-```
-
-| Parameters | Type | Required? |
-| ------------- | ----------------------------------------------- | --------- |
-| [view](#view) | [View](../architecture/views.md) | yes :100: |
-| [data](#data) | anything indexable (i.e. Array, Object, String) | no |
-
-| Return Value | Type |
-| ---------------------------------------------------- | ----- |
-| [virtual node](../architecture/views.md#virtual-dom) | VNode |
-
-`memo()` lets you take advantage of a performance optimization technique known as [memoization](../architecture/views.md#memoization).
-
----
-
-## Parameters
-
-### `view`
-
-A [view](../architecture/views.md) you want [memoized](../architecture/views.md#memoization).
-
-### `data`
-
-The data to pass along to the wrapped view function instead of the [state](../architecture/state.md). The wrapped view is recomputed when the data for it changes.
-
----
-
-## Example
-
-Here we have a list of numbers displayed in a regular view as well as a memoized version of the same view. One button changes the list which affects both views. Another button updates a counter which affects the counter's view and also the regular view of the list but not the memoized view of the list.
-
-```js
-import { h, text, app, memo } from "hyperapp"
-
-const randomHex = () => "0123456789ABCDEF"[Math.floor(Math.random() * 16)]
-const randomColor = () => "#" + Array.from({ length: 6 }, randomHex).join("")
-
-const listView = (list) =>
- h("p", {
- style: {
- backgroundColor: randomColor(),
- color: randomColor(),
- },
- }, text(list))
-
-const MoreItems = (state) => ({ ...state, list: [...state.list, randomHex()] })
-const Increment = (state) => ({ ...state, counter: state.counter + 1 })
-
-app({
- init: {
- list: ["a", "b", "c"],
- counter: 0,
- },
- view: (state) =>
- h("main", {}, [
- h("button", { onclick: MoreItems }, text("Grow list")),
- h("button", { onclick: Increment }, text("+1 to counter")),
- h("p", {}, text(`Counter: ${state.counter}`)),
- h("p", {}, text("Regular view showing list:")),
- listView(state.list),
- h("p", {}, text("Memoized view showing list:")),
- memo(listView, state.list),
- ]),
- node: document.querySelector("main"),
-})
-```
-
----
-
-## Other Considerations
-
-### Performance
-
-Using `memo()` too often will lead to [degraded performance](../architecture/views.md#performance). Only use `memo()` when you know it will improve rendering. When in doubt, benchmark!
-
-### Memo Data Gotcha
-
-When Hyperapp checks memo data for changes it will do index-for-index comparisons between what the data currently is with how it was in the previous state. So, any indexable type like strings and arrays can be compared with one another and in certain edge cases be considered "equal" when it comes to determining if a re-render should happen.
-
-We can modify parts of the example from earlier to illustrate this:
-
-```js
-// ...
-
-const MoreItems = (state) => ({
- ...state,
- list: Array.isArray(state.list)
- ? [...state.list, randomHex()]
- : state.list + "" + randomHex(),
-})
-
-const Increment = (state) => ({
- ...state,
- counter: state.counter + 1,
-
- // The following should cause the memoized view to rerender but it doesn't.
- list: Array.isArray(state.list)
- ? state.list.join("")
- : state.list.split(""),
-})
-
-// ...
-```
diff --git a/docs/api/text.md b/docs/api/text.md
deleted file mode 100644
index 09b2fa83a..000000000
--- a/docs/api/text.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# `text()`
-
-**_Definition:_**
-
-> A function that creates a [virtual DOM node (VNode)](../architecture/views.md#virtual-dom) out of a given value.
-
-**_Import & Usage:_**
-
-You'll normally use it with [`h()`](./h.md).
-
-```js
-import { text } from "hyperapp"
-
-// ...
-
-h("p", {}, text(content))
-```
-
-**_Signature & Parameters:_**
-
-```elm
-text : (String | Number) -> VNode
-```
-
-| Parameter | Type | Required? | Notes |
-| ------------------- | ----------------------------------------------------- | -------------- | --------------------------------------- |
-| [content](#content) | any (sort of), but meaningfully only String or Number | yes :100: | |
-| node | DOM element | prohibited :x: | This is for internal Hyperapp use only! |
-
-| Return Value | Type |
-| --------------------------------------------------------- | ----- |
-| [virtual text node](../architecture/views.md#virtual-dom) | VNode |
-
-You would use `text()` to insert regular text content into your views.
-
-```js
-h("p", {}, text("You must construct additional pylons."))
-```
-
-
-
-Of course, this may include anything relevant from the [current state](../architecture/state.md).
-
-```js
-h("p", {}, text(state.message))
-```
-
-`text()` exists as the way of defining text nodes such that Hyperapp's implementation is kept simpler than it otherwise would have been.
-
----
-
-## Parameters
-
-### `content`
-
-While `content` can technically be anything, what will actually be used for the content of the VDOM element will be the stringified version of `content`. So, using actual strings and numbers makes a lot of sense but using arrays will probably be formatted in a way you don't want and objects won't work well at all.
diff --git a/docs/architecture/actions.md b/docs/architecture/actions.md
deleted file mode 100644
index 8ce739e50..000000000
--- a/docs/architecture/actions.md
+++ /dev/null
@@ -1,246 +0,0 @@
-# Actions
-
-**_Definition:_**
-
-> An **action** is a message used within your app that signals the valid way to change [state](state.md).
-
-An action is implemented by a deterministic function that produces no side-effects which describes a transition between the current state and the next state and in so doing may optionally list out [effects](effects.md) to be run as well.
-
-Actions are dispatched by either DOM events in your app, [effecters](effects.md#effecters), or [subscribers](subscriptions.md#subscribers). When dispatched, actions always implicitly receive the current state as their first argument.
-
-**_Signature:_**
-
-```elm
-Action : (State, Payload?) -> NextState
- | [NextState, ...Effects]
- | OtherAction
- | [OtherAction, Payload?]
-```
-
-**_Naming Recommendation:_**
-
-Actions are recommended to be named in `PascalCase` to signal to the developer that they should be thought of as messages intended for use by Hyperapp itself. It is also recommended to use a verb (for instance `Add`) or a verb-noun phrase (`AddArticle`) for the name. The verb can be either in its imperative form, like `IncrementBy`, `ToggleVisibility`, `GetPizzas` or `SaveAddress`, or in the past tense form, for instance `GotData`, `StoppedCounting` – especially when the action is used for a "final" state transition at the end of an action-effect-chain.
-
----
-
-## Simple State Transitions
-
-The simplest possible action merely returns the current state:
-
-```js
-// Action : (State) -> SameState
-const Identity = (state) => state
-```
-
-It seems useless at first but can be helpful as a placeholder for other actions while prototyping a new app or [component](views.md#components).
-
-Probably the most common way to use an action is to assign it as an event handler for one of the nodes in your view.
-
-```js
-h("button", { onclick: Identity }, text("Do Nothing"))
-```
-
-The next simplest type of action merely sets the state.
-
-```js
-// Action : () -> ForcedState
-const FeedFace = () => 0xfeedface
-```
-
-
-
-
-But you'll most likely want to do actual state transitions.
-
-```js
-// Action : (State) -> NewState
-const Increment = (state) => ({ ...state, value: state.value + 1 })
-
-// ...
-
-h("button", { onclick: Increment }, text("+"))
-```
-
----
-
-## Payloads
-
-Actions can also accept an optional **payload** along with the current state.
-
-```js
-// Action : (State, Payload?) -> NewState
-const AddBy = (state, amount) => ({ ...state, value: state.value + amount })
-```
-
-To give a payload to an action we'll want to use an **action descriptor**.
-
-```js
-h("button", { onclick: [AddBy, 5] }, text("+5"))
-```
-
-### Event Payloads
-
-Actions used as event handlers receive the event object as the default payload.
-
-If we were to use our `AddBy` action without specifying its payload:
-
-```js
-h("button", { onclick: AddBy }, text("+5"))
-```
-
-then it will receive the event object when the user clicks it and will attempt to directly "add" that to our state which would obviously be a bug.
-
-However, if we wanted to make proper use of the event object we have a couple options:
-
-- Rewrite `AddBy` to account for the possibility of receiving an event payload.
-- Or preprocess the event object to make it work with `AddBy` as it is.
-
-The latter option is preferred because it lets our action remain unconcerned with how its payload is sourced thereby maintaining its reusability.
-
-Which brings us to...
-
----
-
-## Wrapped Actions
-
-Actions can return other actions. The simplest form of these basically acts like an alias.
-
-```js
-// Action : () -> OtherAction
-const PlusOne = () => Increment
-```
-
-A more useful form preprocesses payloads to use with other actions. We can make an event adaptor so our primary action can use event data without coupling to the event source.
-
-```js
-// Action : (State, EventPayload) -> [OtherAction, Payload]
-const AddByValue = (state, event) => [AddBy, +event.target.value]
-```
-
-We'll make use of `AddByValue` with an `input` node instead of the `button` from earlier because we want the event that gets preprocessed to have a `value` property we can extract:
-
-```js
-h("input", { value: state, oninput: AddByValue })
-```
-
-You can keep wrapping actions for as long as your sanity permits. The benefit is the ability to chain together payload adjustments.
-
-```js
-const AddBy = (state, amount) => ({ ...state, value: state.value + amount })
-const AddByMore = (_, amount) => [AddBy, amount + 5]
-const AddByEvenMore = (_, amount) => [AddByMore, amount + 10]
-
-// ...
-
-h(
- "button",
- { onclick: [AddByEvenMore, 1] },
- text("+16")
-)
-```
-
----
-
-## Transforms
-
-You may consider refactoring very large and/or complicated actions it into simpler, more manageable functions. If so, remember that actions are just messages and, conceptually speaking, are not composable like the functions that implement them. That being said, it can at times be advantageous to delegate some state processing to other functions. Each of these constituent functions is a **transform** and is intended for use by actions or other transforms.
-
-```js
-const Liokaiser = (state) => ({
- ...state,
- combined: true,
- leftArm: hellbat(state),
- rightArm: guyhawk(state),
- upperTorso: leozack(state),
- lowerTorso: jallguar(state),
- leftLeg: drillhorn(state),
- rightLeg: killbison(state),
-})
-```
-
-
-
----
-
-## Stopping Your App
-
-You can cease all Hyperapp processes by transitioning to an `undefined` state. This can be useful if you need to do specific cleanup work for your app.
-
-```js
-// Action : () -> undefined
-const Stop = () => undefined
-```
-
-Once your app stops, several things happen:
-
-- All of the app's subscriptions stop.
-- The DOM is no longer touched.
-- Event handlers stop working.
-
-A stopped app cannot be restarted.
-
-If you encounter a scenario where your app doesn't respond when you click stuff within it, then your app might have been stopped by mistake.
-
----
-
-## Other Considerations
-
-### Transitioning Array State
-
-An array returned from an action carries [special meaning as already mentioned earlier](effects.md#using-effects). For this reason an actual [array state](state.md#array-state) needs special consideration.
-
-There are a couple of options available:
-
-- Wrap the return state within an [effectful state array](state.md#state-with-effects). Mention also that init: option of app() function must also be wrapped.
-
- ```js
- const ArrayAction = (state) => [[...state, "one"]]
- ```
-
-- Or you can choose a different format for the state by setting it up as an object that contains the the array so actions can work with it like they would with any other object state.
-
- ```js
- const ObjectAction = (state) => ({ ...state, list: [...state.list, "one"] })
- ```
-
-### Nonstandard Usage
-
-- Using an anonymous function for an action has the disadvantage that it has no name for debugging tools to make use of. That's significant because it's recommended that actions have names.
-
-- If you wanted to use curried functions to implement actions then you can use named function expressions.
-
- ```js
- const Meet = (name) =>
- function AndGreet(state) {
- return `${state.salutation}, my name is ${name}.`
- }
- ```
-
-- If you have some special requirements you can customize how actions are [dispatched](dispatch.md).
-
-- Because of the way Hyperapp works internally, anywhere actions can be used literal values can be used instead to directly set state and possibly run effects.
-
- ```js
- h("button", { onclick: 55 }, text("55"))
- h("button", { onclick: [55, log] }, text("55 and log"))
- ```
-
- However, this conflicts with the notion that state transitions happen through the usage of actions. The valid way to achieve the same thing would be:
-
- ```js
- const FiftyFive = () => 55
- const FiftyFiveAndLog = () => [55, log]
- ```
-
- ```js
- h("button", { onclick: FiftyFive }, text("55"))
- h("button", { onclick: FiftyFiveAndLog }, text("55 and log"))
- ```
-
- The [`init`](../api/app.md#init) property of [`app()`](../api/app.md) is the only place where it's valid to either directly set the state or use an action to do it.
-
- That said, this type of usage is fascinating...
-
- ```js
- h("button", { onclick: state.startingOver ? "Begin" : MyCoolAction }, text("cool"))
- ```
diff --git a/docs/architecture/dispatch.md b/docs/architecture/dispatch.md
deleted file mode 100644
index f98785bb3..000000000
--- a/docs/architecture/dispatch.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# Dispatch
-
-**_Definition:_**
-
-> The **dispatch** function controls Hyperapp's core dispatching process which executes [actions](actions.md), applies state transitions, runs [effects](effects.md), and starts/stops [subscriptions](subscriptions.md) that need it.
-
-You can augment the dispatcher to tap into the dispatching process for debugging/instrumentation purposes. Such augmentation is loosely comparable to middleware used in other frameworks.
-
-**_Signature:_**
-
-```elm
-DispatchFn : (Action, Payload?) -> void
-```
-
----
-
-## Dispatch Initializer
-
-The dispatch initializer accepts the default dispatch as its sole argument and must give back a dispatch in return. Hyperapp's default dispatch initializer is equivalent to:
-
-```js
-const boring = (dispatch) => dispatch
-```
-
-In your own initializer you'll likely want to return a variant of the regular dispatch.
-
----
-
-## Augmented Dispatching
-
-A dispatch function accepts as its first argument an [action](actions.md) or anything an action can return, and its second argument is the default [payload](actions.md#payloads) if there is one. The payload will be used if the first argument is an action function.
-
-The action will then be carried out and its resulting state transition will be applied and then any effects it requested to be run will be run.
-
-```js
-// DispatchFn : (Action, Payload?) -> void
-const dispatch = (action, payload) => {
- // Do your custom work here.
- // ...
-
- // Hand dispatch over to built-in dispatch.
- dispatch(action, payload)
-}
-```
-
-## Dispatch recursion
-
-Dispatch is implemented in a recursive fashion, such that if the action dispatched does not represent the next state (or next state with effects), it will use the dispatched action and payload to resolve the next thing to dispatch.
-
-A call to `dispatch([ActionFn, payload])` will recurse `dispatch(ActionFn, payload)`, which will recurse to `dispatch(ActionFn(currentState, payload))`.
-
----
-
-## Example 1 - Log actions
-
-Let's say you need to debug the order in which actions are dispatched. An augmented dispatch that logs each action could help with that, rather than having to add `console.log` to every action.
-
-```js
-const logActionsMiddleware = dispatch => (action, payload) => {
-
- if (typeof action === 'function') {
- console.log('DISPATCH: ', action.name || action)
- }
-
- //pass on to original dispatch
- dispatch(action, payload)
-}
-```
-
----
-
-## Example 2 - Log state
-
-To log each state transformation, we first create a general state middleware and then use it to create an augmented dispatch for state logging:
-
-```js
-const stateMiddleware = fn => dispatch => (action, payload) => {
- if (Array.isArray(action) && typeof action[0] !== 'function') {
- action = [fn(action[0]), ...action.slice(1)]
- } else if (!Array.isArray(action) && typeof action !== 'function') {
- action = fn(action)
- }
- dispatch(action, payload)
-}
-
-const logStateMiddleware = stateMiddleware(state => {
- console.log('STATE:', state)
- return state
-})
-```
-
----
-
-## Example 3 - Immutable state
-
-When learning Hyperapp and during developemt it can sometimes be useful to guarantee states are not mutated by mistake, let's use `stateMiddleware` above to create an augmented dispatch for state immutability:
-
-```js
-// a proxy prohibiting mutation
-const immutableProxy = o => {
- if (o===null || typeof o !== 'object') return o
- return new Proxy(o, {
- get(obj, prop) {
- return immutableProxy(obj[prop])
- },
- set(obj, prop) {
- throw new Error(`Can not set prop ${prop} on immutable object`)
- }
- })
-}
-
-export const immutableMiddleware = stateMiddleware(state => immutableProxy(state))
-```
-
-
-## Usage
-
-The [`app()`](../api/app.md) function will check to see if you have a dispatch initializer assigned to the [`dispatch:`](../api/app.md#dispatch) property while instantiating your application. If so, your app will use it instead of the default one.
-
-The only time the dispatch initializer gets used is once during the instantiation of your app.
-
-Only one dispatch initializer can be defined per app. Consequently, only one dispatch can be defined per app.
-
-Extending the example from above, the dispatch initializer would be used like this:
-
-```js
-import { mwLogState } from "./middleware.js"
-
-app({
- // ...
- dispatch: logActionsMiddleware
-})
-```
-
-And if you wanted to use all custom dispatches together, you can chain them like this:
-
-```js
-import { logActionsMiddleware, logStateMiddleware, immutableMiddleware } from "./middleware.js"
-
-app({
- // ...
- dispatch: dispatch => logStateMiddleware(logActionsMiddleware(immutableMiddleware(dispatch)))
-})
-```
-
-
----
-
-## Other Considerations
-
-- [`app()`](../api/app.md) returns the dispatch function to allow [dispatching externally](../api/app.md#instrumentation).
-
-- If you're feeling truly adventurous and/or know what you're doing you can choose to have your dispatch initializer return a completely custom dispatch from the ground up. For what purpose? You tell me! However, a completely custom dispatch won't have access to some important internal framework functions, so it's unlikely to be something useful without building off of the original dispatch.
diff --git a/docs/architecture/effects.md b/docs/architecture/effects.md
deleted file mode 100644
index 5de06b65a..000000000
--- a/docs/architecture/effects.md
+++ /dev/null
@@ -1,248 +0,0 @@
-# Effects
-
-_**Definition:**_
-
-> An **effect** is a representation used by actions to interact with some external process.
-
-As with [subscriptions](subscriptions.md), effects are used to deal with impure asynchronous interactions with the outside world in a safe, pure, and immutable way. Creating an HTTP request, giving focus to a DOM element, saving data to local storage, sending data over a WebSocket, and so on, are all examples of effects at a conceptual level.
-
-**_Signature:_**
-
-```elm
-Effect : EffecterFn | [EffecterFn, Payload]
-```
-
-**_Naming Recommendation:_**
-
-Effects are recommended to be named in `camelCase` using a verb (for instance `log`) or verb-noun phrase (like `saveAsPDF`) in its imperative form for the name.
-
-## Using Effects
-
-An action can associate its state transition with a list of one or more [effects](#effects) to run alongside the transition. It does this by returning an array containing the [state with effects](state.md#state-with-effects) where the first entry is the next state while the remaining entries are the effects to run.
-
-```js
-import { log } from "./fx"
-
-// Action : (State) -> [NextState, ...Effects]
-const SayHi = (state) => [
- { ...state, value: state.value + 1 },
- log("hi"),
- log("there"),
-]
-
-// ...
-
-h("button", { onclick: SayHi }, text("Say Hi"))
-```
-
-Actions can of course receive payloads and use effects simultaneously.
-
-```js
-// Action : (State, Payload) -> [NextState, ...Effects]
-const SayBye = (state, amount) => [
- { ...state, value: state.value + amount },
- log("bye"),
-]
-
-// ...
-
-h("button", { onclick: [SayBye, 1] }, text("Bye"))
-```
-
-## Excluding Effects
-
-If you don't include any effects in the return array then only the state transition happens.
-
-Here, `OnlyIncrement` both behaves and is used similarly to `Increment` [shown here](actions.md#actual-state-transition):
-
-```js
-// Action : (State) -> [NextState]
-const OnlyIncrement = (state) => [{ ...state, value: state.value + 1 }]
-
-// ...
-
-h("button", { onclick: OnlyIncrement }, text("+"))
-```
-
-Such a single-element array may seem redundant at first but it can come into play if you have an action that conditionally runs effects.
-
-For example, compare this:
-
-```js
-const DoIt = (state) => {
- let transition = { ...state, value: "MacGuffin" }
- if (state.eating) {
- transition = [transition, log("eating")]
- }
- if (state.drinking) {
- transition = Array.isArray(transition)
- ? [...transition, log("drinking")]
- : [transition, log("drinking")]
- }
- return transition
-}
-```
-
-
-
-with this:
-
-```js
-const DoItBetter = (state) => {
- let transition = [{ ...state, value: "MacGuffin" }]
- if (state.eating) {
- transition = [...transition, log("eating")]
- }
- if (state.drinking) {
- transition = [...transition, log("drinking")]
- }
- return transition
-}
-```
-
-Admittedly, these examples are a bit contrived but the latter is less complex.
-
-However, for these examples in particular we can do even better by taking advantage of the fact that any "effects" that are actually falsy values are ignored.
-
-```js
-const DoItBest = (state) => [
- { ...state, value: "MacGuffin" },
- state.eating && log("eating"),
- state.drinking && log("drinking"),
-]
-```
-
-## Defining Effects
-
-Syntactically speaking, an effect takes the form of a tuple containing its [effecter](#effecters) and any associated data.
-
-Technically, an effect can be used directly but using a function that creates the effect is recommended because it offers flexibility with how the tuple is created while looking a little cleaner overall.
-
-```js
-const massFx = (data) => [runNormandy, data]
-```
-
-
-
-## Effecters
-
-**_Definition:_**
-
-> An **effecter** is the function that actually carries out an effect.
-
-**_Signature:_**
-
-```elm
-EffecterFn : (DispatchFn, Payload?) -> void
-```
-
-As with [subscribers](subscriptions.md#subscribers), effecters are allowed to use side-effects and can also manually [`dispatch`](dispatch.md) actions in order to inform your app of any pertinent results from their execution.
-
-It's important to know that effecters are more than just a way to wrap any arbitrary impure code. Their purpose is to be a generalized bridge between your app's business logic and the impure code that needs to exist. By keeping the effecters as generic as we can, we form a clean, manageable separation between what is requested to be done from how that request is done.
-
-To demonstrate this approach take this ill-formed effecter for example:
-
-```js
-// This effecter is ill-formed.
-const runHarvest = (dispatch, _payload) => {
- const tiberium = document.getElementById("tiberium")
- dispatch((state) => ({ ...state, tiberium }))
-}
-```
-
-
-
-Sure it runs, but it's also coupled to our app's state and the element ID being referenced is also hard-coded.
-
-Let's address this by first decoupling our callback action from the effecter by leveraging our ability to give the effecter a payload:
-
-```js
-const runHarvest = (dispatch, payload) =>
- dispatch(payload.action, document.getElementById("tiberium"))
-```
-
-Let's further utilize our payload by using it to pass in data our effecter needs to work:
-
-```js
-const runHarvest = (dispatch, payload) =>
- dispatch(payload.action, document.getElementById(payload.id))
-```
-
-Finally, we should rename the effecter to reflect its generic nature:
-
-```js
-const runGetElement = (dispatch, payload) =>
- dispatch(payload.action, document.getElementById(payload.id))
-```
-
-A well-formed effecter is as generic as it can be.
-
-### Synchronization
-
-Effecters which run some asynchronous operation and wish to report the results of it back to your app will need to ensure that the timing of their communication dispatch happens in alignment with Hyperapp's repaint cycle. This is important to ensure the state is set correctly.
-
-Hyperapp's repaint cycle stays synchronized with the browser's natural repaint cycle, so asynchronous effecters must do the same. The preferred way to do this is with [`requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). If for some reason that method is unavailable, the fallback is [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).
-
-Let's see an example of an ill-formed asynchronous effecter:
-
-```js
-// This effecter is ill-formed.
-const runBrotherhood = async (dispatch, payload) => {
- const response = await fetch(payload.lookForKaneHere)
- const kaneLives = response.json()
- requestAnimationFrame(() => {
- dispatch((state) => ({
- ...state,
- message: kaneLives ? "One vision! One purpose!" : "",
- }))
- })
-}
-```
-
-
-
-Now let's see a more well-formed asynchronous effecter:
-
-```js
-const runSimpleFetch = async (dispatch, payload) => {
- const response = await fetch(payload.url)
- requestAnimationFrame(() => dispatch(payload.action, response.json()))
-}
-```
-
-### Custom Events
-
-The ideal scenario to use custom effects is when your Hyperapp application needs to communicate with a legacy app via custom events.
-
-We can have our Hyperapp application use a custom effect for triggering custom events.
-
-```js
-// ./fx.js
-
-const runEmit = (_dispatch, payload) =>
- dispatchEvent(new CustomEvent(payload.type, { detail: payload.detail }))
-
-export const emit = (type, detail) => [runEmit, { type, detail }]
-```
-
-```js
-import { h, text, app } from "hyperapp"
-import { emit } from "./fx"
-
-app({
- view: () =>
- h("main", {}, [
- h(
- "button",
- {
- onclick: (state) => [
- state,
- emit("outgoing", { message: "hello" })
- ],
- },
- text("Send greetings")
- ),
- ]),
- node: document.querySelector("main"),
-})
-```
diff --git a/docs/architecture/flowchart.md b/docs/architecture/flowchart.md
deleted file mode 100644
index 3c8218b74..000000000
--- a/docs/architecture/flowchart.md
+++ /dev/null
@@ -1,29 +0,0 @@
-```mermaid
-graph TD
- init(" init ")
- init --- |"init:{state}h()
init:[state, effect(s)]
"|j0[ ] --- j1[ ] -->nextState
- init --> |"init:Action
init:[Action, payload?]"|Action
-
- domevent("DOM/synthetic
events
(click/myevent) ") --> viewEvent
- viewEvent((" view
event")) --> Action
- externalEvents("global/external
processes
(window resize) ") --> subscription
- subscription(("subscription")) -->Action
-
- Action[" Action
(state change)"] -->|"OtherAction
[OtherAction, payload?]
"|Action
- Action --- |"NextState
[NextState, ...Effects]"|j2[ ] ---> nextState
-
- nextState((" next
state")) --- j3[ ]
- j3 --> |"view(state)"|newDom(" (re)render
DOM ")
- j3 --> |"subscriptions(state)"|recalcSubs(recalc
subscriptions)
- j3 --> |"(dispatch, Payload?) -> void"|effect
- effect("Effects
(impure code)") -.-> |"dispatch"|dispatchAction("Action")
-
- style j0 height:1px;
- style j0 width:1px;
- style j1 height:1px;
- style j1 width:1px;
- style j2 height:1px;
- style j2 width:1px;
- style j3 height:1px;
- style j3 width:1px;
-```
diff --git a/docs/architecture/state.md b/docs/architecture/state.md
deleted file mode 100644
index 499874e05..000000000
--- a/docs/architecture/state.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# State
-
-**_Definition:_**
-
-> The **state** of your Hyperapp application is the unified set of data that your [views](views.md), [actions](actions.md), and [subscriptions](subscriptions.md) all have access to.
-
-Hyperapp by design offers no strong opinion about how your state should be structured aside from it being unified. This means [components](views.md#components) don't technically possess their own local state but that also means they have direct access to any part of the entire state they need. The user is entrusted with shaping things beyond that.
-
----
-
-## Assignment
-
-When you initially create your app instance with [`app()`](../api/app.md) you get to setup your state with the [`init:`](../api/app.md#init) property. Since it's possible to have several different app instances [active simultaneously](../api/app.md#multiple-apps) it's important to know that each app retains its own separate state.
-
-### State Transitions
-
-Aside from the aforementioned [`init:`](../api/app.md#init) property the only way to affect state is by changing it through the use of [actions](actions.md).
-
-## State With Effects
-
-If you use an array to set the state Hyperapp will interpret this as a special array where the first entry is the state and if there are more entries they will be [effects](effects.md) that need to be run.
-
-So, Hyperapp will apply the state first and then will run the effects in the order they appear.
-
-```js
-[state, log(state), log("MOAR")]
-```
-
-### Array State
-
-If you actually do want to use an array as your state you'll have to wrap it within an effectful state array to make it work.
-
-```js
-[["a", "b", "c"]]
-```
-
-The actions page also [talks about it](actions.md#transitioning-array-state).
-
----
-
-## Visualization
-
-The primary [view](views.md) of your app, as set by the [`view:`](../api/app.md#view) property of [`app()`](../api/app.md), will receive the current state for it to use to determine what gets rendered. Any changes to the state are automatically reflected there as well.
-
----
-
-## Other Considerations
-
-### Serializability
-
-While you can put anything you want in the state we recommend avoiding things that are unserializable such as symbols, functions, and recursive references. This helps to ensure compatibility with things like saving to persistent local storage, or using other tools especially ones that are potentially Hyperapp-specific.
-
-### State Type
-
-You can of course choose to make your state some basic type such as a string or number. However, it's recommended to use an object because of the expressivity it gives you in defining your state shape.
-
-### Direct Mutation
-
-Since we are ultimately using JavaScript you can technically edit parts of the state mutably. However, state changes should be thought of in terms of snapshots: New versions of the state get created to reflect the changes made at a certain moment in time. This perspective naturally calls for immutability.
-
-Fresh new state should be returned from an [action](actions.md). If an action returns nothing your app will [stop](actions.md#stopping-your-app). If you mutate the current state and return it you are returning the same object reference as the state was earlier. Hyperapp cannot tell from this that any changes have occurred. Hence the action will do nothing.
diff --git a/docs/architecture/subscriptions.md b/docs/architecture/subscriptions.md
deleted file mode 100644
index 56fef80e4..000000000
--- a/docs/architecture/subscriptions.md
+++ /dev/null
@@ -1,240 +0,0 @@
-# Subscriptions
-
-**_Definition:_**
-
-> A **subscription** function represents a dependency your app has on some external process.
-
-As with [effects](effects.md), subscriptions deal with impure, asynchronous interactions with the outside world in a safe, pure, and immutable way. They are a streamlined way of responding to events happening outside our application such as time or location changes. They handle resource management for us that we would otherwise need to worry about like adding and removing event listeners, closing connections, etc.
-
-**_Signature:_**
-
-```elm
-Subscription : [SubscriberFn, Payload?]
-```
-
-**_Naming Recommendation:_**
-
-Subscriptions are recommended to be named in `camelCase` prefixed by `on`, for instance `onEvery` or `onMouseEnter` in order to reflect their event handling character.
-
----
-
-## Using Subscriptions
-
-Subscriptions are setup and managed through the [`subscriptions:`](../api/app.md#subscriptions) property used with [`app()`](../api/app.md) when instantiating your app.
-
-```js
-import { onEvery } from "./time"
-
-// ...
-
-app({
- init: { delayInMilliseconds: 1000 },
- subscriptions: (state) => [
- // Dispatch `RequestResource` every `delayInMilliseconds`.
- onEvery(state.delayInMilliseconds, RequestResource),
- ],
-})
-```
-
-You can control if subscriptions are active or not by using boolean values.
-
-```js
-app({
- subscriptions: (state) => [
- state.toBe && onEvery(state.delay, ThatIsTheQuestion),
- state.notToBe || onEvery(state.delay, ThatIsTheQuestion),
- ],
-})
-```
-
-
-
-### Subscriptions Array Format
-
-Hyperapp expects the subscriptions array to be of a fixed size with each entry being either a boolean value or a particular subscription function that stays in the same array position. Using dynamic arrays won't work. Inlining subscription functions also won't work because they would just reset on every state change.
-
-### Subscription Lifecycle
-
-On every state change, Hyperapp will check each subscriptions array entry to see if they're active and compare that with how they were in the previous state. This comparison determines how subscriptions are handled.
-
-| Previously Active | Currently Active | What Happens |
-| ----------------- | ---------------- | -------------------------------------------- |
-| no | no | Nothing. |
-| no | yes :100: | Subscription starts up. |
-| yes :100: | no | Subscription shuts down and gets cleaned up. |
-| yes :100: | yes :100: | Subscription remains active. |
-
-To restart a subscription you must first deactivate it and then, during the next state change, reactivate it.
-
----
-
-## Custom Subscriptions
-
-There may be times when an official Hyperapp subscription package is unavailable for our needs. For those scenarios we'll need to make our own custom subscriptions.
-
-### Subscribers
-
-**_Definition:_**
-
-> A **subscriber** is a function which implements an active subscription.
-
-**_Signature:_**
-
-```elm
-SubscriberFn : (DispatchFn, Payload?) -> CleanupFn
-```
-
-As with [effecters](effects.md#effecters), subscribers are allowed to use side-effects and can also manually [`dispatch`](dispatch.md) actions in order to inform your app of any pertinent results from their execution.
-
-Subscribers can be given a data `payload` for their use.
-
-Well-formed subscribers, as it is with effecters, should be as generic as possible. However, unlike with effecters, they should return a function that handles cleaning up the subscription if it gets cancelled.
-
-### Example
-
-Let's say we're embedding our Hyperapp application within a legacy vanilla JavaScript project.
-
-Somewhere within the legacy portion of our project a custom event gets emitted:
-
-```js
-// Somewhere in our legacy app...
-
-const triggerSpecialEvent = () => {
- dispatchEvent(new CustomEvent("secret", { detail: 42 }))
-}
-
-// ...
-
-triggerSpecialEvent()
-```
-
-
-
-Our embedded Hyperapp application will need a custom subscription to be able to deal with custom events:
-
-```js
-// ./subs.js
-
-const listenToEvent = (dispatch, props) => {
- const listener = (event) =>
- requestAnimationFrame(() => dispatch(props.action, event.detail))
-
- addEventListener(props.type, listener)
- return () => removeEventListener(props.type, listener)
-}
-
-export const listen = (type, action) => [listenToEvent, { type, action }]
-```
-
-In case you're wondering why `listenToEvent()`'s listener is using `requestAnimationFrame`, it has to do with [synchronization](actions.md#synchronization).
-
-Now we can use our custom subscription in our Hyperapp application. Since it will be embedded we'll wrap our call to [`app()`](../api/app.md) within an exported function our legacy app can make use of:
-
-```js
-import { h, text, app } from "hyperapp"
-import { listen } from "./subs"
-
-const Response = (state, payload) => ({ ...state, payload })
-
-export const myApp = (node) =>
- app({
- init: () => ({ payload: null }),
- view: ({ payload }) =>
- h("main", {}, [
- payload && h("p", {}, text(`Payload received: ${JSON.stringify(payload)}`)),
- ]),
- subscriptions: () => [listen("secret", Response)],
- node: document.querySelector("main"),
- })
-```
-
----
-
-## Other Considerations
-
-### Destructuring Gotcha
-
-Since a well-formed subscriber returns a cleanup function, it's possible that the cleanup function would want to communicate back to your app that the cleanup took place.
-
-```js
-const listenToEvent = (dispatch, props) => {
- const listener = (event) =>
- requestAnimationFrame(() => dispatch(props.action, event.detail))
-
- addEventListener(props.type, listener)
- return () => {
- removeEventListener(props.type, listener)
- dispatch(props.action, "Todo
+
+ {state.todos.map(({ id, value, done }) => (
+
+ {todos.map(todo =>
+)
+```
+
+Note that when using JSX, components [must be capitalized](https://facebook.github.io/react/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized) or contain a `.` in their name.
+
+## Children Composition
+
+Components receive children elements in the second argument.
+
+```jsx
+const Box = ({ color }, children) => (
+ Hello, {name}!
+ 0
+
+
+
+
{title}
+
+``` + +Check out [hyperapp/awesome](https://github.com/hyperapp/awesome#apps-and-boilerplates) for templates to help you get started. diff --git a/docs/introduction/installation.md b/docs/introduction/installation.md new file mode 100644 index 000000000..aeed891b2 --- /dev/null +++ b/docs/introduction/installation.md @@ -0,0 +1,27 @@ +## Installation + +Install with npm or Yarn. + +
+npm i hyperapp +
+ +Then with a module bundler like [Rollup](https://github.com/rollup/rollup) or [Webpack](https://github.com/webpack/webpack), use as you would anything else. + +```jsx +import { h, app } from "hyperapp" +``` + +Or download directly from [unpkg](https://unpkg.com/hyperapp), [jsDelivr](https://cdn.jsdelivr.net/npm/hyperapp@latest/dist/hyperapp.js), or [CDNJS](https://cdnjs.com/libraries/hyperapp). + +```html + +``` + +Then find it in `window.hyperapp`. + +```jsx +const { h, app } = hyperapp +``` + +We support all ES5-compliant browsers, including Internet Explorer 10 and above. diff --git a/docs/reference.md b/docs/reference.md deleted file mode 100644 index 6c4bcd57a..000000000 --- a/docs/reference.md +++ /dev/null @@ -1,41 +0,0 @@ -# Reference - -## API - -- [`h()`](api/h.md) creates a virtual DOM node (VNode) that gets rendered. -- [`text()`](api/text.md) turns a string into a VNode. -- [`app()`](api/app.md) initializes a Hyperapp app and mounts it. -- [`memo()`](api/memo.md) creates a special VNode that is lazily rendered. - -## Architecture - -- [State](architecture/state.md) represents your application's data. -- [Views](architecture/views.md) represent your state visually. -- [Actions](architecture/actions.md) cause state transitions and trigger effects. -- [Effects](architecture/effects.md) are triggered by actions to interact with external processes. -- [Subscriptions](architecture/subscriptions.md) dispatch actions in response to external events. -- [Dispatch](architecture/dispatch.md) controls action dispatching. - - [Flowchart](architecture/flowchart.md) showing the general flow of a hyperapp app. - -
- -- [Action](architecture/actions.md): An app behavior that transitions state and invokes effects. -- [Action Descriptor](architecture/actions.md#payloads): A tuple representing an action with its payload. -- [Component](architecture/views.md#components): A view with a specific purpose. -- [Dispatch Function](architecture/dispatch.md#dispatch): The process that executes actions, applies state, and calls effects. -- [Dispatch Initializer](architecture/dispatch.md#dispatch-initializer): A function that controls dispatch. -- [Effect](architecture/effects.md): A generalized encapsulation of an external process. -- [Effecter](architecture/effects.md#effecters): A function that carries out an effect. -- [Event Payload](architecture/actions.md#event-payloads): A payload specific to an event. -- [Memoization](architecture/views.md#memoization): In Hyperapp, the delayed rendering of VNodes. -- [Mount Node](api/app.md#node): The DOM element that holds the app. -- [Payload](architecture/actions.md#payloads): Data given to an action. -- [State](architecture/state.md): The unified set of data your Hyperapp application uses and maintains. -- [State Transition](architecture/state.md#state-transitions): An evolutionary step for the state. -- [Subscriber](architecture/subscriptions.md#subscribers): A function that carries out a subscription. -- [Subscription](architecture/subscriptions.md): A binding between the app and external events. -- [Top-Level View](architecture/views.md#top-level-view): The main view which is given the state. -- [VDOM](architecture/views.md#virtual-dom): The virtual DOM, an in-memory representation for the DOM of the current page. -- [View](architecture/views.md): A function describing the desired DOM, represented by a VNode, as a function of the current state. -- [Wrapped Action](architecture/actions.md#wrapped-actions): An action that is returned by another action. diff --git a/docs/tutorial.md b/docs/tutorial.md deleted file mode 100644 index 658eb9e36..000000000 --- a/docs/tutorial.md +++ /dev/null @@ -1,730 +0,0 @@ -# Tutorial # - -If you're new to Hyperapp, this is a great place to start. We'll cover all the essentials and then some, as we incrementally build up a simplistic example. To begin, open up an editor and type in this html: - -```html - - -
- - - - -
-