Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@composi/core

composi310MIT2.7.9TypeScript support: included

A JavaScript library for creating websites, PWAs and hybrid apps.

angular, component, composi, frontend, hybrid, hyperscript, jsx, library, progressive web app, pwa, react, vuejs, virtual dom

readme

Composi

GitHub top language npm (scoped) GitHub

  1. Introduction
  2. Installation
  3. h
  4. render
  5. Component Replaces Target Element
  6. Keys
  7. Lifecycle Hooks
  8. onmount
  9. onupdate
    1. onunmount
    2. run
    3. Default Program
    4. Optional Program Methods
    5. Actions for Update
    6. Tagged Unions
    7. Summary

Introduction

Composi is a framework for creating desktop and mobile apps. With Composi you can create a dynamic website with reactive components, a progressive web app, or a hybrid app for mobile app stores. Visit the website.

Composi core is small, barely 2KB gzipped. It therefore loads fast. Its virtual DOM updates components efficiently and quickly. It has an API very similar to React, while mainting it's emphasis on simplicity, size, and ease of learning. In fact, you can learn everything you need to know to build apps with Composi core in an hour.

Composi core supports functional components. You use props to pass in values, data or events. A functional component can have child components. And a parent can pass props down to its children. There is no two-way data binding. Data flows down from parent to child.

A component's markup is written with JSX. This means you can create custom tags to organize your component's DOM structure. If you prefer, you can instead use the h function to define the component's markup with hyperscript. In fact, at build time the JSX is converted to h functions.

Unlike React, properties are standard HTML versions--class instead of className, etc., and inline events are lowercase, not camel case. However, self-closing tags do need to have a final back slash to be valid.

CDN

If you want, you can load @composi/core from a CDN and use it in the browser without a need to install anything or have a build process. You can do this for fast prototyping. This works on any modern evergreen browser: Chrome, Edge, Firefox or Safari. To do this, create a folder and in it create an index.html file and a JavaScript file. At the top of the JavaScript file you will import @composi/core as follows:

import { h, render, run, union } from 'https://unpkg.com/@composi/core@0.10.0/dist/composi-core.mjs?module'
import { htm } from 'https://unpkg.com/htm.mjs?module'
import { mergeObjects } from 'https://unpkg.com/@composi/merge-objects@1.1.0/src/index.js?module'

Visit the website for more details on how to use @composi/core in the browser when loading from a CDN.

Installation

To add Composi core to your project, install from NPM:

npm i -D @composi/core

After installing, you can import the h and render functions to create function components and render them to the DOM. Continue reading to learn how to use them.

h

A hyperscript function that lets you define virtual nodes. When you transpile your code with Babel, it uses this function to convert JSX into virtual nodes that Composi core understands. It takes from one to three arguments:

  1. type--the element name: div, p, ul, etc.
  2. props--an object literal of key/value pairs
  3. children--an array of child nodes, either text or other virtual nodes.

Example

import { h, render } from '@composi/core'

const Title(greet) {
  return h(
    'header',
    {
      class: 'heading'
    },
    [
      h(
        'h1',
        {
          title: greet
        },
        [
          'Hello, ',
          greet,
          '!'
        ]
      )
    ]
  )
}

// Render component:
let title = render(Title('World'), '.header')

If there are not props for an element, you can use {}. You could also just use null, but {} is two characters shorter.

The above hyperscript function is equivalent to the following JSX:

const Title({greet}) {
  return (
    <header class='heading'>
      <h1 title={greet}>Hello, {greet}!</h1>
    </header>
  )
}

// Render component:
let title = render(<Title greet='World' />, 'header')

JSX will always be more compact and readable than writing out a hyperscript function. However, you can pick the style you prefer.

render

You use the render function to mount and update a functional component. It takes two arguments: the component to render, and the container to render it in.

import { h, render } from '@composi/core'

function Title({greet}) {
  return (
    <header>
      <h1>Hello, {greet}!</h1>
    </header>
  )
}

// Mount the component:
render(<Title greet='Everybody'/>, document.querySelector('#title'))

When providing a container to render in, you can use either a DOM node reference, or a valid selector value:

// Mount the component on header tag:
render(<Title greet='Everybody'/>, document.querySelector('header'))

// Or just pass in a selector:
render(<Title greet='Everybody'/>, 'header')

The first time the render processes a component, it caches the component's vnode on its container. For all other renders, the render function grabs the vnode from the container to diff and patch the DOM agains the newest version of the component.

import { h, render } from '@composi/core'

function Title({greet}) {
  return (
    <header>
      <h1>Hello, {greet}!</h1>
    </header>
  )
}

// Render the component the first time.
render(<Title greet='Joe'/>, 'header')

// Update the component 5 seconds later.
setTimeout(() => {
  render(<Title greet='Everybody Else'/>, 'header')
}, 5000)

Component Replaces Target Element

When you use the render function, Composi takes the DOM element you provide and hydrates it with the functional component. As such, it is not practically to render a component directly into the document body. You'll need to provide a stub element to render your app. Something like this will work fine:

<body>
  <div id='app'></div>
</body>

Then you could target it like this:

render(<App {...{state}}/>, '#app')

If you want to be able to output several different components into the same container, such as the body tag above, just provide a separate stub for each component that you want to render.

Keys

For repetitive siblings, such as lists, Composi core lets you use keys to uniquely identify each item in the collection. If the collection is state, you do not need to use keys. However, if you intend to delete or otherwise change the order of items in the collection, you'll want to provide keys for each item. This helps Composi core track the DOM elements with the virtual DOM against the changed data. Not providing keys can result in unpredictable re-renders of the collection in the DOM.

Keys must be unique values for that colleciton. Many databases already provide ids or uuids for items in a collection. Check you datasource. Using array index values as keys should not be done. If you delete or change the order of the array items, the index values will not match the array items. This will lead to unpredictable rendering of the colleciton in the DOM. Avoid this. Instead, if you data does not have ids already, use some scheme to add ids before passing the data to the functional component.

When you assign a key to a list item, it does not get added to the actual rendered element. It is only a property of the virtual node.

Example


import { h, render } from '@composi/core'

// List component.
// Notice how we assign a key directly on the list item:
function List({data}) {
  return (
    <ul>
      {
        data.map(item => <li key={item.key}>{item.value}</li>)
      }
    </ul>
  )
}

// Notice the unique id for each item in the array:
const fruits = [
  {
    id: 101,
    value: 'Apples'
  },
  {
    id: 102,
    value: 'Oranges'
  },
  {
    id: 103,
    value: 'Bananas'
  }
]

// Render the list:
render(<List data={fruits} />, document.querySelector('#list'))

Lifecycle Hooks

Composi core provides three lifecycle hooks. These are registred on the element that you want to track. The expect a callback to execute when they occur. Each callback will get certain arguments passed to it based on which lifecyle hook it is.

  1. onmount
  2. onupdate
  3. onunmount

onmount

The onmount lifecycle hook lets you do something right after a component is mounted. It also gives you access to the element on which the hook is registered.

onmount gets one argument--the element that the hook is on. This lets you do things such as setting up event listeners, starting timers, or accessing the element's child nodes.

Example

import { h, render } from '@composi/core'

// Define clock component:
function Clock({time}) {
  if (!time) time = new Date()
  return (
    <div onmount={initClock}>
      <h3>The Current Time</h3>
      <p>It is {time.toLocaleTimeString()}.</p>
    </div>
  )
}

// Start tick interval after mount:
function initClock() {
  const timerId = setInterval(
    () => {
      // Re-render the clock at each tick.
      render(<Clock />, '#clock')
    }),
    1000
  )
}

// Mount the clock.
render(<Clock />, '#clock')

In the above example, the component will be updated every second. But only the text node with the time value will change due to virtual DOM patching.

Since we are re-rendering the clock every second, we recapture the result as well. This enables Composi to update the DOM efficiently, keeping DOM thrashing to a minimal.

Accessing the Component's DOM

You can also use onmount to access the DOM of the component to set focus on an input:

import { h, render } from '@composi/core'

function Form() {
  function setFocus(input) {
    input.focus()
  }
  return (
    <form >
      <p>
        <input onmount={setFocus} type='text' />
      </p>
      <p>
        <button type='submit'>Add</button>
      </p>
    </form>
  )
}

When the above component renders, the input will have focus.

onupdate

This gets three arguments--the element the hook is on, the old props and the new props.

Example

import { h, render } from '@composi/core'

function Title({greet}) {
  function announce(el, oldProps, newProps) {
    console.log(`The old value was: ${oldProps.greet}. The new value is: ${newProps.greet}.`)
  }
  return (
    <nav>
      <h1 onupdate={announce}>Hello, {greet}!</h1>
    </nav>
  )
}

// Mount the component:
render(<Title greet='World'/>, 'header')

// Update component in 5 seconds:
setTimeout(() => {
  render(<Title greet='World'/>, document.body, title)
}, 5000)

About Props

When onupdate fires on an element, it only has access to the props that were assigned to that element. So, in order to access whatever data you want to be checking, make sure that data is available on that element as a prop. The prop doesn't need to be actually used with that element.

onunmount

This gets two arguments--the element the hook is registered on and done, which is a callback that you will need to call after you finish whatever you are doing. Calling done() allows the unmount to continue. Failing to call it will prevent the element from being removed from the DOM. This can lead to serious problems when Composi tries to reconcile the DOM with the virtual DOM.

Example

import { h, render } from '@composi/core'

function List({data}) {
  function deleteItem(id) {
    // Because we are reassigning the filerted results,
    // the variable fruits needs to be initialized with `let`, not `const`.
    // Otherwise you'd get an error about assigning to a read only property.
    fruits = data.filter(item => item.id != id)
    render(<List data={fruits}/>, '#list')
  }

  // Animate list item when deleted.
  function animate(item, done) {
    item.style.cssText = 'transition: all 1s ease-out; height: 0px; transform: translateX(-300px);'
    // Don't forget to call `done()` or the element won't be removed!
    // Use setTimeout to delay the remove until after the animation ends.
    setTimeout(() => done(), 1000)
  }
  return (
    <ul>
      {
        data.map(item => <li key={item.id} onunmount={animate}>
          <span>{item.value}</span>
          <button class='delete' onclick={() => deleteItem(item.id)}>X</button>
        </li>)
      }
    </ul>
  )
}

render(<List data={fruits}/>, '#list')

hydration

Since the render function uses an existing DOM element, hydration is built in--no need for something special. This means you can have Content created on the server. Then when the browser loads, have Composi target it as the node for the component in the render function.

import { h, render } from '@composi/core'

// Convert server-rendered list into a virtual node:

function List({data}) {
  return (
    <ul>
      {
        data.map(item => <li key={item.key}>{item.value}</li>)
      }
    </ul>
  )
}

const fruits = [
  {
    key: 101,
    value: 'Apples'
  },
  {
    key: 102,
    value: 'Oranges'
  }
]

// Pass in serverList virtual node as third argument.
// This will let Composi patch the DOM by calculating the difference between the functional component and serverList.
let list = render(<List data={fruits} />, '#server-genereated-content')

Run

@composi/core run creates a runtime for Redux-style state management for functional components. To use it, you do need to import it:

import { h, render, run } from '@composi/core'

Run takes one argument, the program to run. This is where it gets interesting. A program has five methods. The first three are required, the last two are optional:

  1. init
  2. update
  3. view
  4. subscriptions - optional
  5. done - optional

Init is a function that returns the program's state and optionally an effect to run at startup. That's why its called init.

Update is like a Redux reducer. It executes various actions conditionally. The can modify and return the programs state. When it returns the state, it gets passed to the view.

View is a function that can return some kind of presentation of the state. This is where you would use render to output a functional component.

Default Program

With init, view and update you have everything you need to make a valid program that you can run:

import { h, render, run } from '@composi/core'
// Minimal valid program to run:
const program = {
  init() {},
  update() {},
  view() {}
}
run(program)

Optional Program Methods

Subscriptions is an optional method that contains effects to run when the program starts. Using @composi/core's batchEffects function it is possible to run more than one effect at the same time, say, start a timer and fetch data at the same time. Subscriptions is optional. In fact, it's just a more convenient and explicit way of running an effect the same way passing an effect as the second value in init is. Many people will feel more comfortable using a dedicated function for subscriptions that simply tagging on an extra value to init.

Done is an optional method that allows you to do clean when you stop a program, such as stopping timers, animations, etc. When you pass a program to run, it returns a function that you can use to stop a program. The following is a simple program that does only one thing--it starts a setInterval. At any time we can stop the program and terminate the interval. Notice how we use done to do this.

import { h, render, run } from '@composi/core'

const section = document.querySelector('#clock')

// Define clock component for view:
function Clock(state) {
  return (
    <h2>The time is {state}</h2>
  )
}

// Define effect to run at program start.
// It will start a loop that runs every second,
// sending a message to the update function.
// Put it after state in init:
let setIntervalID
function startLoop(getState, send) {
  let count = 0
  setIntervalID = setInterval(() => {
    console.log(count++)
    send('update-time')
  }, 1000)
}

// Define funtion to stop setInterval:
function stopLoop() {
  clearInterval(setIntervalID)
}

function action(state, msg) {
  if (msg === 'update-time') {
    state = new Date().toLocaleTimeString()
    // Return new state to re-render view:
    return [state]
  }
}

// Assemble program:
const program = {
  init() {
    return [new Date().toLocaleTimeString()]
  },
  view(state) {
    return render(Clock(state), '#clock')
  },
  update(state, msg, send) {
    return action(state, msg)
  },
  // Setup subscription:
  subscriptions(getState, send) {
    return startLoop
  }
  // ADD DONE METHOD FOR EFFECT CLEANUP:
  done() {
    stopLoop()
  }
}

// Run program.
// While doing so, capture program in stopProgram variable,
// so we can stop it.
const stopProgram = run(program)
// Some time later we stop the program.
// Doing so invokes the `done` function, thereby terminating the loop.
stopProgram()

Each property expects certain arguments.

Init is a function that returns an array. The first entry in that array is the state for the program. The second entry, which is optional, is an effect to run at startup. This might be a setInterval timer, or a code to fetch data.

Update get two arguments: message and state. Message is any message sent to it by the view. Message get sent when events are triggered in the UI, possibly by the user.

View gets passed two arguments: state and send. The state is used by the view's template function to render. The send function is used to send messages from the view to the update method. You let the update method know what action occured and any data that the action might need.

Here's an simple clicker example:

import { h, render, run } from '@composi/core'
const section = document.querySelector('section')

// Counter for view:
function Counter({state, send}) {
  return (
    <p>
      <button class='counter' onclick={() => send()}>{state}</button>
    </p>
  )
}

// Assemble program:
const program = {
  // Set initial state:
  init() {
    return [0]
  },
  update(state, msg) {
    return [state + 1]
  },
  view(state, send) {
    return render(<Counter {...{state, send}} />, '#counter')
  }
}

// Run program:
run(program)

Live example on Codepen

Actions for Update

The above example was very simplistic, but it shows how to send a message from the view to the update method. Although we sent a message, it was not of any value, so it was undefined. If your program is very simple and only has one action like this, then you can just send an empty message. However, if your program needs more than one action/message, you'll need to use a standard interface for the messages you send. In the following Todo list example we implement several actions for the update method by sending message objects that we can test to see which one was received:

import { h, render, run } from '@composi/core'

const section = document.querySelector('section')

// State for program:
const state = {
  newKey: 104,
  inputVal: '',
  fruits: [
    {
      key: 101,
      value: 'Apples'
    },
    {
      key: 102,
      value: 'Oranges'
    },
    {
      key: 103,
      value: 'Bananas'
    }
  ]
}

// Actions for Update:
function actions(state, msg) {
  switch (msg.type) {
    case 'add-item':
      const value = msg.inputValue
      if (value) {
        state.fruits.push({ key: state.newKey++, value })
        return [state]
      } else {
        alert('Please provide a value!')
        return [state]
      }
      break
    case 'delete-item':
      state.fruits = state.fruits.filter(item => item.key != msg.key)
      return [state]
      break
  }
}

// Functional list component for view:
function List({state, send}) {
  let inputValue
  const focusInput = input => {
    input.focus()
  }
  const getInputValue = e => (inputValue = e.target.value)
  return (
    <div class='list-container'>
      <p class='list-form'>
        <input value={state.inputVal} onupdate={focusInput} onchange={getInputValue} type="text"/>
        <button class='add-item' onclick={() => send({type: 'add-item', inputValue})}>Add</button>
      </p>
      <ul>
        {
          state.fruits.map(item => (
            <li key={item.key}>
              <span>{item.value}</span>
              <button class="delete-item" onclick={() => send({
                type: 'delete-item',
                key: item.key
              })}>X</button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

// Assemble program together:
const program = {
  init() {
    return [state]
  },
  update(state, msg) {
    return actions(state, msg)
  },
  view(state, send) {
    return render(<List {...{state, send}} />, '#todo-list')
  }
}

// Run program:
run(program)

Live example on Codepen

In the above example, we now have a dedicated actions function that handles different possible updates: add-item, delete-item. Notice that an action always returns state:

return [state]

If an action fails to return state, the program will throw an exception and the view will fail to render. Even if you make no changes to state, you have to return it.

The program's view method gets two arguments, the state and the send function. This is used interally by the runtime. You use it in the view to send messages to the update method. These messages can be objects with a type and data for the action to use.

Although this is manageable, we can make this actions and events more implicit by using tagged unions. This is explained next.

Tagged Unions

@composi/core's union function lets you create tagged unions. A tagged union allows you to associate one value with another value. For actions and events this will match the sent message type to the action function to run.

The union function takes a variable number of arguments, separated by commas. This returns a tagged union object. It has a method called match that allows you to check what union you are dealing with a run a function.

Here's the previous todo list redone using tagged unions. Notice that in the view, when we send, we send a tagged union function. This makes it clearer what the event is doing. When we pass a tagged union function to an event's send method, it invokes that function to get a message object with a type and data to the update function. So tagged unions are doing the same as we did in the first example of the todo list, but the show what is being invoked inside the update function.

import { h, render, run } from '@composi/core'

const section = document.querySelector('section')

// The State.
// An object defining the state for the app.
const state = {
  newKey: 104,
  inputValue: '',
  fruits: [
    {
      key: 101,
      value: 'Apples'
    },
    {
      key: 102,
      value: 'Oranges'
    },
    {
      key: 103,
      value: 'Bananas'
    }
  ]
}

// Tagged union for actions,
// This will match string values to functions.
// Capture the union in the Msg object.
const Msg = union('updateInputValue', 'addItem', 'deleteItem')

// Desturcture tagged union variables:
const {updateInputValue, addItem, deleteItem} = Msg


// Business Logic.
// Intercept actions dispatched by view.
// Use those actions to transform state.
// Then return the new state.
// That will cause the view to update.
function actions(state, msg, send) {
  return Msg.match(msg, {
    updateInputValue: value => {
      state.inputValue = value
      return [state]
    }
    addItem: () => {
      if (state.inputValue) {
        state.fruits.push({ key: state.newKey++, value: state.inputValue })
        return [state]
      } else {
        alert('Please provide a value!')
      }
    },
    deleteItem: key => {
      state.fruits = state.fruits.filter(item => item.key != key)
      return [state]
    }
  })
}

// The view: a list component.
// I knows nothing about state or update.
// It catches user interactions and
// dispatches the results.
// It also uses lifecycle events to handle
// visual effects, such as input focus.
function List({state, send}) {
  let inputValue
  const focusInput = input => {
    input.focus()
  }
  return (
    <div class='list-container'>
      <p class='list-form'>
        <input
          value={state.inputValue}
          onupdate={focusInput}
          oninput={e => send(updateInputValue(e.target.value))} type="text"
        />
        <button class='add-item' onclick={() => send(addItem())}>Add</button>
      </p>
      <ul>
        {
          state.fruits.map(item => (
            <li key={item.key}>
              <span>{item.value}</span>
              <button
                class="deleteItem"
                onclick={() => send(deleteItem(item.key))}
              >X</button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}

// Assemble program to run:
const program = {
  init() {
    return [state]
  },
  view(state, send) {
    return render(<List state={...{state, send}} />, '#todo-list')
  },
  update(state, msg, send) {
    return actions(state, msg, send)
  }
}

// Run program:
run(program)

Live example on Codepen

As you can see in the above example, tagged unions make the connection between view events and update actions more implicit.

No Matching Message

By default if you pass a message whose type does have a match in the action methods provided, the union will log an error alerting you to this fact. This can happen is you send a message object and mispell the message type.

You can override this behavior and provide your own default behavior for what to do when there is no match. To do so, just provide an optional third argument to the Message Union match method:

function actions(state, msg, send) {
  const prevState = {...state}
  return Msg.match(
    msg,
    {
      DoIt: () => {
        prevState.successMessage = 'We are doing it!'
        return prevState
      }
    },
    () => {
      prevState.errorMessage = `Ooops! I got the following message: ${msg.type}. Is this a typo?`)
      return prevState
    }
  )
}

Summary

Composi is all about components. These provide a great way to organize your code into modular and reusable chunks. The virtual DOM means you never have to touch the DOM to change the structure.

Because Composi uses JSX, there are many similarities to React patterns. Please note that Composi is not a React clone. It is not trying to be compatible with React and the React ecosystem the way Preact and Inferno do. Composi core does not have PropTypes. Events are not synthetic. Functional component have three lifecycle hooks, whereas React functional components have none. However, because of using a virtual DOM and JSX, the similarities are greater than the differences. The API is very small--comprising two functions: h and render and three lifecycle hooks that are similar to the ones React has for class components. If you are familiar with React, Inferno or Preact, you can note the differences and be productive with Composi core in less than a half hour.

changelog

composi/core Changelog

2.7.9 (April 27, 2020)

Updated dependencies.

2.7.6 (January 5, 2020)

src/types.js

  • Simplified @typedef declarations.

2.7.6 (January 3, 2020)

src/runtime.js

  • Updated updateView function to use AND/OR expressions instead of ternary operators.

2.7.5 (December 18, 2019)

.eslinrc.json

  • Updated Eslint rules to include modern JavaScript features like const and destructuring.

src/h.js, src/runtime.js, src/union.js, src/vdom.js

  • Updated to use more const and destructuring.

2.7.4 (December 14, 2019)

src/index.d.ts

  • Renamed to jsx.d.ts.

src/types.js

  • Amalgamated all types into a single file. Use import and namespaced batch imports of types in source files.

package.json

  • Update TypeScript to version 3.7.3. This added new capabilities for type support, enabling batch imports mentioned above, as well as automatic generation of d.ts files from JavaScript using JSDoc comments.

types: constants.d.ts, effects.d.ts, fragment.d.ts, h.d.ts, index.d.ts, render.d.ts, runtime.d.ts, types.d.ts, union.d.ts, vdom.d.ts, vnode.d.ts

  • These are all auto-generated when running build script.

2.7.3 (December 10, 2019)

src/union.js, src/vdom.js

  • Replaced some if/else statements with logical expressions where possible.

  • Refactored some functions in vdom into arrow functions for simplicity.

2.7.2 (December 8, 2019)

src/h.js, src/runtime.js, src/union.js, src/vdom.js

  • Used logical expressions to remove if/else blocks for cleaner and easier to read code.

2.7.1 (December 7, 2019)

src/union.js

  • Use destructuring to simplify union message management.

2.7.0 (November 29, 2019)

src/runtime.js

  • Updated behavior of send function. Now you can pass a destructured tag union function without inoviking it. You can also provide an optional second parameter as a data payload.

  • In the example below, notice how we just send AddItem, whereas with UpdateInputValue we provide a second argument for the data:

const Msg = union('UpdateInputValue', 'AddItem')
const {UpdateInputValue, AddItem} = Msg

function List({state, send}) {
  return (
    <div>
      <input oninput={e => send(UpdateInputValue, e.target.value)} type='text'/>
      <button onclick={() => send(AddItem)}>Add Item</button>
    </div>
  )
}

2.6.3 (November 28, 2019)

src/union.js

  • Added error checking for when user tries to use "match" as a method. The union function produces and object that always has a match method, so trying to create a tagged union that can handle a "match" message can be problematic. The developer will now get a console error message warning not to create a union with type "match".

2.6.2 (November 26, 2019)

.prettierignore

  • Added .pretterignore to control what files Prettier formats.

package.json

  • Add Prettier config options to package.json.

2.6.2 (November 26, 2019)

src/union.js

  • Added error checking to union match method. Now if the message type sent does not match the provided action methods, it will log an error.

  • Added catchAll for message union match method as an optional third argument. This overrides the default error handling mentioned above. You can use this to handle a default case when the message sent does not match any of the methods provided. You could use this to provide a default action to perform or to log an error:

Msg.match(
  msg,
  {
    DoIt: () => {
      return prevState
    }
  },
  // Handle case where message doesn't match:
  () => {
    console.error(`You sent the message "${msg.type}", but it does not have a match in the actions provided.`)
    prevState.errorMessage = `You sent the message "${msg.type}", but it does not have a match in the actions provided.`
    return prevState
  }
)
// Send wrong message:
send({type: 'EatPizza'})
// Will result in above message being logged to console:
// You sent the message "EatPizza", but it does not have a match in the actions provided.

## 2.6.1 (November 18, 2019)

### index.d.ts

* Updated IntrinsicAttributes interface to have property `children` of type `Children` (child vnodes) as the final argument for a JSX functional component. This enables function components to allow accepting a final arguemnt of `children`, which provides a slot for insertion of random child content into the component using open and close tags around the child:

```javascript
function Title({ title }, children) {
  return (
    <header>
      <h1>Hello, {title}!</h1>
      {
        children
      }
    </header>
  )
}

<Title title="World"><h2>Extra Text</h2></Title>

2.6.0 (November 17, 2019)

src/effects.js

  • Changed return type of batch from any to void.

src/runtime.js

  • Switch around parameters for subscriptions. The new order is subscriptions: (send, getState). getState is now optional. getState is used so rarely with subscriptions it was annoying always having to provide it as the first argument. Now you can have a subscript with just the argument send:
function effect(send) {
  // code here...
}
  • Made both send and getState as optional arguments for subscriptions since it is possible to have a subscription that does not access state or send a message.

tests/runtime.html

  • Updaed tests for runtime to accomodate change in how subscriptions handle parameters.
function effect(send) {
  /...
}

2.5.13 (November 15, 2019)

src/h.js

  • Refactored h function to return VNode by default. Previously the defaul was wrapped in a conditional check.

src/union.js

  • Broke out match function from createUnion function and gave it proper types.

  • Provide overall type coverage for tags and tagged union result.

src/vdom.js

  • In createNode function got rid of need for type coercion by converting node.type to string first.

2.5.12 (November 13, 2019)

package.json package-lock.json

  • Added @babel/preset-modules as build dependency. This tells Babel to only target browsers that support importing es modules. This results in a smaller bundle size, knocking of up to 250 bytes by eliminating unnecessary polyfills.

.babelrc

  • Modified .babelrc to use @babel/preset-modules as plugin instead of @babel/preset-env. This results in smaller bundle sizes for modern browser that support es modules.

2.5.11 (November 11, 2019)

src/effects.js, src/index.js, src/index.d.ts

  • Provided short form for batchEffects as batch. Less typing same thing. The longer form of batchEffects is still available and works for backwards compatibility.

src/vdom.js

  • Removed automatic lower casing of inline events. This is so users can use DOM level 3 custom events with camel case names. We lower cased the inline events to make it easier for people use to React events to use. But since the default inline events are lower case, we are dropping this for DOM level 3 support.

2.5.10 (October 31, 2019)

src/index.d.ts, src/index.js

  • Exported Props type.

src/vnode.js

  • Improved types for onmount, onupdate and onunmount.

2.5.9 (October 31, 2019)

src/index.js

  • Updated type exports to include GetState.

src/index.d.ts

  • Added index.d.ts to src so that it gets imported automatically by Editors for type information. This is paricularlly to expose JSX types for functional components, as well as the types of @composi/core.

src/runtime.js, src/vnode.js

  • Updated for better type handling.

2.5.8 (October 29, 2019)

src/vdom.js

  • Added function areNotEqual which is used to check if the old props and new props of a VNode are identical. If they are not identical then onupdate executes, otherwise not.

2.5.7 (October 27 2019)

tsconfig.json, package.json

  • Added tsconfig to simplify NPM script checkjs. No it is just "checkjs": "tsc"

src/vnode.js

  • Modified definition of VNode to optionally be an empty object literal. This was necessary for properly typing or h. This change enables h working properly with the JSX d.ts file provided by the @composi/create-composi-app for JSX Intellisense in Composi projects.

2.5.6 (October 26 2019)

src/effects.js, src/union.js

  • Improved types for both effects.js and union.js.

2.5.5 (October 25, 2019)

  • Removed jsconfig.json and replaced with tsconfig.json. The tsconfig has type check options. This removes need for NPM script test checkjs to use options, simplifying it to just "checkjs": "tsc".

2.5.4 (October 25, 2019)

src/runtime.js

  • Fixed issue where type for send required a parameter when in fact it is optional. Yes, you can just send an empty message, but then your update can only handle that one case. Works for very simple programs.

tscofig.json

  • Added tsconfig.json file. This simplified the NPM checkjs script to just 'tsc'.

2.5.3 (October 24, 2019)

.vscode/settings.json

  • Added settings.json file for VSCode to set up live type linting for source code.

runtime.js, vdom.js, vnode.js

  • Improved internal types for runtime.js, vdom.js and vnode.js

2.5.2 (October 23, 2019)

src/union.js

  • Simplified union code and removed unused feature for default behavior. This was never used or useful for anything.
  • Added error handling for when message does not have expected structure.

2.5.1 (October 19, 2019)

package.json

  • Removed unnecessary dependency on @composi/merge-objects.

2.5.0 (October 18, 2019)

src/runtime.js

  • Updated how the runtime works. Now init does not require brackets for state. Just return the state you want to use. Same for actions--just return the new state. This means that you can no longer execute an optional effect in the init method to run at startup. If you want to execute an effect at startup, use the subscriptions method. That's what it's for. Similarly, you can no longer return an effect with state in an action. Instead send a message for another action before returning state.

test/runtime.html

  • Updated runtime test to handle changes to how state get returned now.

2.0.1 (October 15, 2019)

src/render.js

  • Fixed so when render mount function component with onmount hook on existing element, the onmount hook fires.

src/vdom.js

  • Made sure that read-only properties like list, form, etc. are rendered as attributes.

text/fragment.html, h.html, render.html, runtime.html

  • Fixed tests to hanlde new vdom patching method.

2.0.0 (October 7, 2019)

src/vdom.js

  • Refactored virtual DOM for speed improvement. The new approach now directly hydrates and element in the DOM. Previously it required a container into which it appended the rendered functional component. This meant only one component could exist in a container. Now rendering is executed directly on the provided element. This means multiple components can be rendered in the same container as long as each has its own stub to use.

  • Improved diffing for faster renders.

src/render.js

  • Due to changes in the virtual DOM, the render function now only takes two arguemnts, the component to render and an element to hydrate or update.

src/vnode.js

  • The hydrate function was removed since hydration is now automatic when the render function is called the first time.

src/vnode.js

  • Dropped the hydrate and removeWhiteSpaceNodes function and updated both createVNode and v for changes in vdom parsing.

1.6.15 (September 29, 2019)

test/runtime.html

  • Updated runtime tests to handle latest version of subscriptions properly.

1.6.14 (September 27, 2019)

  • Made improvements to JSDocs comments for better Intellisense for h, union and runtime types.

1.6.13 (September 27, 2019)

src/index.js

  • Made types State, Message and Send importable by projects.
  • Cleaned up type export.

1.6.13 (September 27, 2019)

  • Make types State, Message and Send importable by projects. Cleaned up type export.

src/index.js

  • Made type Program defined in src/runtime.js available to importing in projects like this:
/**
 * @type {import('@composi/core').Program}
 */
const program = {
  init() {},
  view(state, send) {},
  update(state, msg, send) {}
}

1.6.12 (September 26, 2019)

src/runtime.js

  • Cleaned up how subscriptions are initialized.

  • Refactored types for runtime program for better Intellisense and type coverage.

1.6.11 (September 9, 2019)

index.js

  • Added import of createVNode from vnode.js to index.js, even though it isn't being exported. This is to get around a bud in TypeScript parser that fails to get types imported with @typedef {import('./vnode').VNode} VNode format. The indirection of the index.js file causes TypeScript to cast JSDoc type imports as any. Importing vnode.js here makes its types avaiabale to TypeScript so that when it find the type imports in other files, it knows what they are. This then provides complete type safety for h and render functions.

h.js

  • Fixed typo in comment.

1.6.10 (August 30, 2019)

rollup.config.js

  • Added back in build for UMD package. This is needed for use in only JavaScript editors like Codepen, JSBin, etc.n

1.6.9 (August 27, 2019)

  • Updated to use the latest version of @composi/mergeObjects
  • This version now properly clones Sets and Maps, as well as merges them together. Of course, you can only merge Sets or Maps. You cannnot merge these with other object types.

1.6.8 (August 6, 2019)

  • Bumped version for publishing purposes.

1.6.7 (August 6, 2019)

rollup.config.js

  • Removed UMD build

test/fragment.html, test/h.html, test/render.html, test/runtime.html

  • Switched from using UMD bundle to load Composi for tests to using ES 6 module syntax.

1.6.6 (August 6, 2019)

package.json

  • Updated version of merge-objects dependency due to security vulnerability.

1.6.5 (August 6, 2019)

rollup.config.js

  • Added UMB build back in because Mocha tests need it!

src/runtime.js

  • Set initial value for isFirstRun to true for first run instead of false since technically it will always be the frist run initially.

1.6.4 (August 1, 2019)

rollup.config.js

  • Removed unnecessary UMD bundle from build.

src/runtime.js

  • Switched around properties of program JSDoc type comments to match actual usage. (They were out of order.)

1.6.3 (July 4, 2019)

src/h.js

  • Simplified childNodes assignment.
  • Removed unnecessary parameter in createVNode call.
  • createVNode now has default values of null for key and flag parameters.

tests/h.html

  • Updated tests to reflect changes in h.js.

src/vdom.js

  • Improved old and new VNode comparison in patch so that onupdate lifecycle hook only fires when the props have changed.

src/vnode.js

  • Instead of assigning default value to createVNode function inside its body, moved assignment as default value in the call signature itself.
  • Marked flag parameter as optional.

1.6.2 (June 15, 2019)

test/render.html

  • Added delay for render test that needed a longer delay before accessing the updated DOM.

1.6.2 (June 11, 2019)

package.json, package-lock.json

  • Updated merge-objects dependency version.

1.6.1 (June 7, 2019)

src/render.js

  • Now taking the VNode passed as an argument to the render function and clone it to save on the component container element as the value of property previousVNode.

src/vdom.js

  • Now the patchElement function checks the new previousVNode property on the parent element to see if the new VNode is identical to the older vision. If they are it exits the patch immediately. Previously, the diff was checking a plain VNode against one which resulted from patching. Problem was, patching made the saved VNode always have DOM values for element, where as a fresh VNode has elements with a value of null. Using previousVNode allows the diff to check the resulting new VNode with the previous version before patching adds elements. This allows for quick exiting from the diff if there are no differences.

The save VNode in the vnode property of the continer is still used for patching, since it holds the last DOM render information.

1.6.0 (June 5, 2019)

src/vdom.js

  • Renamed variables for clarifying their identities better.
  • lastChildren = oldVNodeChildren
  • lastChildStart = oldVNodeChildStart
  • lastChildEnd = oldVNodeChildEnd
  • nextChildren = newVNodeChildren
  • nextChildStart = newVNodeChildStart
  • nextChildEnd = newVNodeChildEnd

1.6.0 (May 25, 2019)

src/runtime.js

  • Created a function getState that gets exposed to subscriptions. This allows subscription to access the current state of a program. Previously subscriptions got passed a reference to state as their first argument. However, this was state as it was at the time the program started. Subscriptions had no way to know the current state of a program at any given time. Providing subscriptions with getState solves this problem. Now an action can change a value in state and a subscription can check for a change in that value in a loop, after which the subscription can do something, such as canceling a loop, etc.
  • getState is now the first argument of a subscription, followed by send:
const program = {
  init() {
    return [state]
  }
  subscriptions(getState, send) {
    return effect
  }
}
function effect(getState, send) {
  // Access state from the program:
  const currentState = getState()
  // Do something with currentState...
}
  • Using getState also lets you store any references to a setInterval, web sockets or services that you implement in subscriptions and want to affect from an action. You can expose them on the program's state so you can access them from other actions.

  • Never use this to directly change a program's state. Instead send the data as the value of a message for an action to handle.

src/vdom.js

  • Refactored updateElement function to handle onmount and onupdate separately. Previously both were getting passed the same parameters: element, oldProps and newProps. Passing props as arguments for onmount makes no sense. Added a conditional so onmount only gets passed a reference to the mounted element. onupdate still gets element, oldProps and newProps.

test/runtime.html

  • Updated runtime test to use the new getState function in subscriptions.

README.md

  • Fixed erroneous urls. These were pointing to older version of Composi in a different Github group. Now they point to the correct repository.

1.5.0 (May 8, 2019)

src/vdom.js

  • Switched parameters around for unmount lifecycle hook. The correct order is function(element, done). This makes unmount feel more inline with onmount and onupdate, which get the element as the first argument.

test/render.html

  • Updated onmount lifecycle test for changes to onunmount parameter order.

README.md

  • Updated README to document changes to onunmount parameter order.

1.4.0 (May 7, 2019) - no version change

README.md

  • Documented new union function call signature.

1.4.0 (May 7, 2019)

src/effects.js

  • Changed batchEffects argument to not require enclosing effects in array. Use of spread operator means just passing in effects separated by commas: const batchedEffects = batchEffects(effect1, effect2, effect3)
  • Same thing when batching subscriptions: const batchedSupcriptions = batchEffects(sub1, sub2, sub3)

src/union.js

  • Changed union argument to not require enclosing message names in array. Use of spread operator means just passing in messages separated by commas: const Msg = union('updateInputValue', 'addItem', 'deleteItem').

src/vdom.js

  • Added back in empty block conditional check for comparing oldVNode and newVNode in patchElement function. This helps prevent certain edge cases where a vnode might be undefined, particularly when dealing with creating new text nodes.
  • Moved function removeWhiteSpaceNodes before hydrate function to avoid need for hoisting.

test/runtime.html

  • Updated tests to use new arrayless arguments for union and batchEffects functions.

1.3.8 (May 6, 2019)

src/vdom.js - setProp function

  • Added in read-only attributes to setProp function to set this is properties instead of attributes.

test/render.html

  • Updated render tests to check read-only attributes being set.

src/vdom.js - removeChildren function

  • Renamed node variable to vnode in removeChildren function, since that’s what it is.

1.3.7 (Apr 29, 2019)

src/index.js

  • Hydrate gets used internally by the render function when a third argument for the element to hydrate is provided, so no need to expose it.

test/render.html

  • Updated render test to reflect this change.

1.3.6 (Apr 28, 2019)

src/vdom.js

  • Removed empty check for oldVNode and newVNode equality and instead added check for their inequality at end.

1.3.5 (Apr 27, 2019) - no version change

package.json && package-lock.json

  • Removed unnecessary, unused dependency: @composi/get-type.

1.3.5 (Apr 26, 2019)

src/effects.js

  • Removed unused mapEffects function. Was never used by anything, so time to go.

src/render.js

  • Added check in hydration to see if container has a ‘vnode’ property. If it does, we know this has already been hydrated and use it. Otherwise we hydrate the element. This prevents unnecessarily re-hydrating a component by mistake.

src/vdom.js

  • updateElement function now also checks for ‘selected’ value for updating properties.

src/vnode.js

  • Rolled functionality of ‘vnodeFromChild’ directly into hydrate function. No need for separate function.

1.3.4 (Apr 17, 2019)

src/vnode.js

  • Previously a vnode element property could be null or undefined. They are now always null by default.
  • Added new function: removeWhiteSpaceNodes. This is used during hydration to remove whitespace nodes before creating a virtual node. That way, if the server-rendered node is the same as the client-rendered vnode, they should match, reducing patch work.

test/h.html

  • Updated h test for vnode changes.

1.3.3 (Apr 13, 2019) - no version change

src/vnode.js

  • Use TEXT_NODE for nodeValue of 3.

1.3.3 (Apr 9, 2019)

package.json & package-lock.json

  • Update to use latest version of @composi/merge-objects 1.2.3.

1.3.2 (Apr 4, 2019)

package.json & package-lock.json

  • Updated version of merge-objects dependency to 1.2.2.

1.3.1 (Apr 3, 2019)

src/effects.js

  • batchEffects was not passing state and send to effects.
  • This batched subscriptions in particular were not getting access to state and send properly because of flaw in batchEffects.
  • batchEffects now exposes state and send to its effects.

test/runtime.html

  • Updated runtime tests to use new exposed state and send parameters in effects.

1.3.0 (Mar 31, 2019)

src/runtime.js

  • Enabled effects to have access to program state.
  • Exposed send function to effects.
  • Gave subscriptions access to state.
  • Exposed send function to subscriptions.

test/runtime.html

  • Added tests for changes to effects and subscriptions.

1.2.0 (Mar 24, 2019)

src/fragment.js

  • Previous Fragment was a separate package. Now it is part of Composi core.
  • Updated Fragment to handle a single argument of an array of nodes.

src/index.js

  • Fragment being exported as part of composi/core

test/fragment.js & test/index.js

  • Added tests for Fragment.

1.1.9 (Mar 20, 2019)

src/vdom.js

  • Removed unnecessry prop type checks from setProp function.
  • Simplified check for null or false in props to detemine when to remove attribute in setProp function.
  • Removed unnecessary isRecycled check from updateElement function.
  • Removed check for RECYCLED_NODE from patchElement function.

1.1.8 (Mar 5, 2019)

src/runtime.js

  • Fixed bug affecting subscriptions in runtime programs. 1. The send function wasn't getting passed to batched effects when used as subscriptions. Fixed.

test/runtime.html

  • Added test for batched subscriptions.

1.1.7 (Mar 3, 2019) - no code changes

  • Version bump for changes to how lifecycle hooks are implemented.

README.md

  • Minor edits to README.

1.1.6 (Feb 28, 2019)

src/constants.js

  • Created a constant named LIFECYCLE. This is for holding lifecycle hooks.

src/vdom.js

  • Refactored all methods to use the new constant LIFECYCLE.

1.1.6 (Feb 22, 2019) - no version change

src/render.js

  • Text formatting clean up.

1.1.6 (Feb 22, 2019) - no version change

src/render.js

  • Added param type VNode for render function.

1.1.6 (Feb 18, 2019)

src/vdom.js

  • Style prop should accept either an object of properties and values or a string of normal CSS to define component styles.

1.1.5 (Feb 16, 2019) - no version change

src/union.js

  • Cleanup of union function code.

1.1.5 (Feb 5, 2019) - no version change

src/package.json

  • Added gzip for ES6 module version (mjs).

1.1.5 (Feb 2, 2019) - no version change

test/runtime.html

  • Fixed runtime test that needed longer delay to finalize result for assertion.

1.1.5 (Jan 15, 2019) - no version change

src/vnode.js

  • Mention in JSDoc comment that hydrate creates a virtual node from a DOM element.

1.1.5 (Jan 13, 2019)

LICENSE

  • Updated license date.

1.1.4 (Dec 20, 2018)

src/runtime.js

  • When the program subscriptions method was empty, encluding it caused throws during runtime in browser because there is no subscription to invoke.
  • Resolved this by checking that the program includes a subscription and also returns an effect to execute.

1.1.3 (Dec 19, 2018)

package.json * package-locked.json

  • Bumped dependency versions.

1.1.2 (Dec 14, 2018)

src/runtime.js

  • Subscriptions now get state and send passed to them automatically, just like view. This lets you access state from a subscription, and send a message to an effect when necessary.

test/runtime.html

  • Updated runtime tests to subscription changes.

1.1.1 (Dec 14, 2018) - no version change

src/vdom.js

  • Fixed typo in JSDoc comment.

1.1.1 (Dec 13, 2018)

src/runtime.js

  • subscriptions were being re-executed every time there was a state change. They should only run during program startup. Fixed.

1.1.0 (Dec 13, 2018) - no version change

src/vdom.js

  • Clarification in JSDoc comment about subscriptions as method of runtime program.

1.1.0 (Dec 13, 2018)

src/runtime.js

  • Added shortcut subs for subscriptions. Less typing, less likely to misspell.

test/runtime.html

  • Added test for use of subs for subscriptions.

1.0.0 (Nov 30, 2018) - no version change

src/runtime.js

  • Isolated subscription execution to avoid overwritting effects from update.

1.0.0 (Nov 30, 2018) - no version change

test/runtime.html

  • Updated runtime test for addition of subscriptions.

1.0.0 (Nov 30, 2018)

src/runtime.js

  • Added Subscriptions API. Subscriptions provide way to run effects when a runtime program first starts.

src/runtime.js

  • Update signature now takes state first, then message.

test/runtime.html

  • Updated runtime tests to reflect API changes.

README.md

  • Updated README for new signature for update function.

0.10.3 (Nov 30, 2018)

  • Removed dist version to avoid conflicts when merging V1 branch.

0.10.3 (Nov 30, 2018)

  • Latest build.

1.0.0-beta.0 (Nov 30, 2018) - no version change

src/effects.js & src/runtime.js

  • Added Subscriptions API.
  • The subscriptions method is for setting up effects that run during the program start. These are the same as running an effect as the second value of the array returned by init. However, subscriptions does the same thing in a more explicit way. Easier for people to understand how to implement startup subscriptions when the name is subscriptions.

1.0.0-beta.0 (Nov 27, 2018) - no version change

src/runtime.js

  • Updated order for parameters in update function.

test/runtime.html

  • Updated tests for update function parameter order changes.

README.md

  • Documented changes to update function parameter order.

1.0.0-beta.0 (Nov 27, 2018) - no version change

src/runtime.js

  • update funtion parameter order is reversed. Changes match init and view parameter order where state is the first value, followed by effect, send or message.

1.0.0-beta.0 (Nov 27, 2018)

src/runtime.js

  • update function parameter order reversed so that state comes first.

0.10.3 (Nov 27, 2018)

package.json & package-locked.json

** Bumped version or npm-run to avoid security vulnerability.

src/render.js

  • Renamed private variable from cont to container for understability.

0.10.2 (Nov 26, 2018) - no version change

README.md

  • Fixed menu typo.

0.10.2 (Nov 25, 2018)

src/union.js

  • Simplified code for tagged unions.

src/runtime.js

  • Added more detail for JSDoc comments.

0.10.1 (Nov 23, 2018)

package.json & package-locked.json

  • Added mjs version for use in browsers.

README.md

  • Added information about how to use mjs module version in the browser.

0.10.0 (Nov 12, 2018)

src/runtime.js

  • Exposing "send" as program static function of program allows sending message to program update method from other contexts. This was introduced to facilitate easier integration with @composi/router. Now in a router action you can send a message to the program to affect state which the program update actions can deal with for rendering the proper subview for the route.

test/runtime.js

  • Added test for static version of send function.

0.9.6 (Nov 9, 2018) - no version change

package.json & package-latest.json

  • Updated to latest version of prettier.

0.9.6 (Nov 9, 2018)

src/effects.js

  • batchEffects was sometimes failing when an argument was falsy. Fixed.

0.9.5 (Nov 5, 2018) - no version change

README.md

  • Fixed bad links.

0.9.5 (Nov 4, 2018) - no version change

src/effects.js

  • Simplified mapEffect function by using arrow function.

0.9.5 (Nov 4, 2018)

src/mapEffects.js

  • Renamed mapEffects.js to effect.js

src/index.js

  • Updated import of renamed effects.js functions.

src/effects.js

  • Added new function: batchEffects. This allows you to batch effects together. When they are executed in the order they are batched.

test/runtime.html

  • Added test for batched effects.

0.9.4 (Nov 4, 2018) - no version change

src/runtime.js

  • Reverted order change in updateView function that introduced bug.

0.9.4 (Nov 4, 2018) - no version change

  • Checked in dist versions that got missed in previous checkin.

0.9.4 (Nov 4, 2018) - no version change

src/runtime.js

  • Refactor of updateView function.

0.9.4 (Nov 4, 2018) - no version change

src/index.js

  • Tests require that hydrate be exported!

src/runtime.js

  • Simplified how run function returns.

0.9.4 (Nov 4, 2018) - no version change

src/index.js

  • No need to export hydrate function.

0.9.4 (Nov 4, 2018)

src/h.js

  • Refactored to use while loop instead of for.

src/union.js

  • Refactored to use while loop instead of for.
  • Use arrow function to return union.

0.9.3 (Nov 4, 2018)

src/runtime.js

  • Renamed private variable from change to updateView.

0.9.2 (Nov 4, 2018) - no version change

dist/composi-core2.js

  • Removed this file - giblet.

0.9.2 (Nov 4, 2018)

src/render.js

  • Added error checking for when user provides invalid container for render function.
  • Composi will now log an error and bail when an invalid container argument is used with the render function. Will output an error message detailing the reason for failure to render, along with the selector used.

test/render.html

  • Fixed bug in runtime test.

0.9.1 (Nov 4, 2018)

src/runtime.js

  • Refactored runtime so init method can be a NOOP.
  • Normally program.init needs to return an array. But now you can make a default program with noop methods:
const program = {
  init() {},
  view() {},
  update() {}
}

test/runtime.html

  • Update test for null init return value.

0.9.0 (Nov 3, 2018)

src/runtime.js

  • Updated how the run function handles state.
  • Previously a program's init was just an array assignment. Now its a function that returns a tuple of state and effect: init() { return [state, effect]}

test/runtime.html

  • Updated init tests for changes to how it returns state.

README.md

  • Updated to include changes to how init returns state.

0.8.6 (Nov 2, 2018)

src/vdom.js

  • Fixed onupdate bug. onupdate was not getting passed the previous props. Fixed.

0.8.5 (Nov 2, 2018) - no version change

README.md

  • README edit.

0.8.5 (Nov 1, 2018) - no version change

  • Checkin dist build. Was missed in last checkin.

0.8.5 (Nov 1, 2018)

src/runtime.js

  • Modified return of state by update actions to enable immutable data. Previously an action just returned state to the view. Now when you return state, it also reassigns it to the program's state. This lets you clone the state inside the action, make changes and then return it. In so doing you update the program state and the view.

package.json & package-locked.js

  • Update dependency on @composi/merge-objects to latest version.

0.8.4 (Oct 31, 2018)

src/h.js

  • Fixed issue for SVG image rendering. Needed to restore empty block in h function.

0.8.4 (Oct 23, 2018)

  • Checked in dist build, left out in last checkin.

0.8.2 (Oct 29, 2018)

src/vdom.js

  • Fixed returning null for component bug. patch function was trying to attach element to vnode even when the vnode was null. Fixed by checking if element exists first.

0.8.1 (Oct 29, 2018)

src/index.js

  • Added export of mapEffects.

0.8.0 (Oct 29, 2018)

  • Add runtime for creating encapsulated programs with state management.

src/runtime.js

  • Added new file. This exposes run function. This creates a runtime for a program. This provides a Redux-style state manager for functional components.
  • A runtime program has four parts:

  • Init: holds state and runs effects at startup.

  • update: handles actions for modifying application state.
  • view: creates representation of state.
  • done: method to cleanup environment before program stops.

src/mapEffect.js

  • Added new file.
  • mapEffect makes it easy to share an effect between a parent program and a child program for inter-program communication.

src/union.js

  • Added new file.
  • The function “union” creates tagged unions. These are used to simplify invoking update actions from the view and handling them in the program’s update method.

test/runtime.js

  • Added new file. Includes tests for new runtime environment for program state management.

0.7.2 (Oct 22, 2018) - no version change

src/h.js

  • No empty blocks.

0.7.2 (Oct 20, 2018)

  • General clean up. Set Eslint to allow reassignment of parameters, enabling elimination of a bunch of variables.

src/vdom.js

  • Refactored virtual DOM functions, fixing some erroneous flag uses.

src/vnode.js

  • Minor code cleanup and variable renaming.

src/h.js

  • Switched to using Reflect.get to get properites from objects. Reflect doesn't throw if the property doesn't exist.

src/render.js

  • Simplified render function.

0.7.1 (Oct 18, 2018)

src/render.js

  • Added hydrateThis as type parameter in JSDoc comments.

0.7.0 (Oct 18, 2018) - no version change

src/render.html

  • Fixed lifecycle tests that were failing.

0.7.0 (Oct 18, 2018)

  • Refactored patch algorithm for simplicity.

  • render now takes two arguments, the component and its container.

  • No need for mount, render mounts and updates the component.
  • Removed separate unmount function. Was not useful and was too problematic. Instead use boolean logic to manage when a component renders.
  • render function now caches the last rendered vnode on the component’s container. Each time you render a component, it checks the container for the cache and uses that to patch the DOM efficiently.
  • Only one component can be rendered in a container, but you can make a parent wrapper to compose many components to render them all in one container.
  • To hydrate a server-rendered element, pass its DOM reference or string selector as the third argument for render.
  • Updated tests for all changes.

src/unmount.js

  • Deleted file as no longer necessary. See above changes to API.

0.6.0 (Oct 9, 2018) - no vesion change

src/unmount.js & src/vdom.js

  • Refactored code to fix eslint error about parameter reassignment.

0.6.0 (Oct 9, 2018)

src/vdom.js

  • Previously the patch algo returned a vnode which you had to capture and return to the next render.
  • Now patch returns the element base of the component. As such, you capture on the first render only. After that, just render.
  • You do need to pass the component element as the thrid parameter during re-renders, but no re-capture any more.
  • Changes to patch required updates to render, unmount, hydrate, removeElement and updateElement.

test/render.html

  • Updated tests for changes.

0.5.5 (Oct 5, 2018)

src/vdom.js

  • Fixed error in JSDoc coment for setProp function.

package.json & package-locked.json

  • Updated @composi get-type and merge-objects to latest versions.

0.5.4 (Oct 5, 2018)

.eslintrc.json

  • Updated eslint rules.

src/h.js, src/vdom.js, src/vnode.js

  • Updated source code to resolve Eslint errors.

package.json & package-locked.json

  • Updated @composi/merge-objects to version 1.0.6 to resolve issues with merge bugs.

0.5.3 (Oct 4, 2018)

  • Bumped version for publishing to NPM.

0.5.2 (Oct 4, 2018) - no version change

package.json

  • Added publishConfig for publishing to NPM.

0.5.2 (Oct 4, 2018) - no version change

package.json & package-locked.json

  • Updted to use latest version of @composi/merge-objects.

0.5.2 (Sep 27, 2018)

package.json & package-locked.json

  • Made @composi/get-type and @composi/merge-objects as dependencies.

0.5.1 (Sep 26, 2018)

src/vdom.js

  • Added error catching for attempt to render array of elements into DOM.

test/render.html

  • Updated render test for array error.

src/h.js

  • Props in h function should be optional.

rollup.config.js

  • Rollup now creating minified dist version.

0.5.0 (Sep 26, 2018) - no version change

package.json

  • Updated package name for scoped NPM package.

0.5.0 (Sep 26, 2018) - no version change

README.md

  • Fixed typo in README.

0.5.0 (Sep 26, 2018)

  • Converted Composi into scoped packages. @composi/core encompasses the virtual DOM and functional components.

  • constants.js are values used by @composi/core.

  • h.js provides hypersrcipt function for defining virtual nodes.
  • vnode.js provides functions to create virtual element nodes and virtual text nodes.
  • vdom.js provides virtual DOM for diffing and patching the DOM.
  • render.js provides function to mount function components in the DOM and update them in place.
  • unmount.js provides function to remove a mounted component form the DOM.
  • Included mocha/chai tests for h, render, lifecycle hooks and unmount.