Javascript (TypeScript) library for building web user interfaces

Overview

ivi · GitHub license codecov

ivi is a javascript (TypeScript) library for building web user interfaces.

Status

Maintenance mode. Bug fixes and documentation updates only.

Quick Start

Hello World

The easiest way to get started with ivi is to use this basic example on CodeSandbox.

import { _, render } from "ivi";
import { h1 } from "ivi-html";

render(
  h1(_, _, "Hello World!"),
  document.getElementById("app"),
);

render() function has a standard interface that is used in many Virtual DOM libraries. First argument is used to specify a Virtual DOM to render, and the second one is a DOM node that will be used as a container.

Virtual DOM API in ivi is using factory functions to instantiate Virtual DOM nodes.

Factory functions for HTML elements are declared in the ivi-html package.

h1() function will instantiate a "Virtual DOM" node for a

element.

_ is a shortcut for an undefined value.

Documentation

Operations ("Virtual DOM")

Virtual DOM in ivi has some major differences from other implementations. Events and keys are decoupled from DOM elements to improve composition model. Simple stateless components can be implemented as a basic immediately executed functions, DOM events can be attached to components, fragments or any other node.

Internally, all "Virtual DOM" nodes in ivi are called operations and has a type Op.

type Op = string | number | OpNode | OpArray | null;
interface OpArray extends Readonly<Array<Op>> { }

Element Factories

All factory functions that create DOM elements have an interface:

type ElementFactory<T> = (className?: string, attrs?: T, children?: Op) => OpNode<ElementData<T>>;

ivi-html package contains factories for HTML elements.

ivi-svg package contains factories for SVG elements.

import { _, render } from "ivi";
import { div } from "ivi-html";

render(
  div(_, _, "Hello World"),
  document.getElementById("app")!,
);

Element Prototypes

Element prototypes are used to create factories for elements with predefined attributes.

import { _, elementProto, render } from "ivi";
import { input, CHECKED } from "ivi-html";

const checkbox = elementProto(input(_, { type: "checkbox" }));

render(
  checkbox(_, { checked: CHECKED(true) }),
  document.getElementById("app")!,
);

Fragments

All virtual dom nodes and component root nodes can have any number of children nodes. Fragments and dynamic children lists can be deeply nested.

const C = component((c) => () => (
  [1, 2]
));

render(
  div(_, _, [
    [C(), C()],
    [C(), C()],
  ]),
  document.getElementById("app")!,
);
Fragments Memoization

Fragments in ivi can be memoized or hoisted like any other node. Because ivi doesn't use normalization to implement fragments, memoized fragments will immediately short-circuit diffing algorithm.

const C = component((c) => {
  let memo;

  return (title) => (
    div(_, _, [
      h1(_, _, title),
      memo || memo = [
        span(_, _, "Static"),
        " ",
        span(_, _, "Fragment"),
      ],
    ])
  );
});

Components

All components has an interface (component) => (props) => VDOM.

Outer function is used to store internal state, creating dataflow pipelines, attaching hooks and creating an "update" function. It is important that outer function doesn't have any access to the props to prevent unexpected "memory leaks". component is an opaque object, it is used as a first argument for almost all component functions like invalidate(), useEffect() etc.

Internal "update" function passes input data through dataflow pipelines and returns a Virtual DOM.

{ let counter = 0; const ticker = useEffect(c, (interval) => { const id = setInterval(() => { counter++; invalidate(c); }, interval); return () => clearInterval(id); }); return (interval) => ( ticker(interval), div(_, _, `Counter: ${counter}`), ); }); render( Counter(1000), document.getElementById("app"), );">
import { _, component, invalidate, render } from "ivi";
import { h1 } from "ivi-html";

const Counter = component((c) => {
  let counter = 0;

  const ticker = useEffect(c, (interval) => {
    const id = setInterval(() => {
      counter++;
      invalidate(c);
    }, interval);
    return () => clearInterval(id);
  });

  return (interval) => (
    ticker(interval),

    div(_, _, `Counter: ${counter}`),
  );
});

render(
  Counter(1000),
  document.getElementById("app"),
);
const Counter = component((c) => {
  let counter = 0;
  // ...
  return () => vdom;
});

component() function creates Virtual DOM factory functions for component nodes. All component factory functions has an interface Factory(props).

In the outer function we are declaring internal state counter.

  const ticker = useEffect(c, (interval) => {
    // ...
    return () => cleanup;
  });

useEffect() creates a function that will be used to perform side effects. Side effect functions can optionally return a cleanup function, it will be automatically invoked when component is unmounted from the document or when input property interval is modified.

  const ticker = useEffect(c, (interval) => {
    const id = setInterval(() => {
      counter++;
      invalidate(c);
    }, interval);
    return () => clearInterval(id);
  });

Side effect function ticker() registers a timer function that is periodically invoked and increments counter from the internal state. When internal state is modified, we need to trigger an update for the component. To trigger an update, we are using invalidate() function. Invalidate function will mark component as dirty and enqueue a task for dirty checking.

Periodic timers registered with setInterval() function should be unregistered when they are no longer used. To unregister periodic timer we are creating and returning a cleanup function () => clearInterval(id).

  return (interval) => (
    ticker(interval),

    div(_, _, `Counter: ${counter}`),
  );

The final step for a component is to create an "update" function, it should pass input data through dataflow pipelines and return a Virtual DOM. Update function will be invoked when component is invalidated or component properties are modified.

Stateless Components

One of the unique features in ivi is that it doesn't store any magic properties like keys on "Virtual DOM" nodes. Decoupling magic properties from "Virtual DOM" nodes allows us to use immediately invoked functions as stateless components.

( key(id, Link(`#${id}`, id)) ))), document.getElementById("app"), );">
const Link = (href, children) => a("link", { href }, children);
const LINKS = [1, 2];

render(
  TrackByKey(LINKS.map((id) => (
    key(id, Link(`#${id}`, id))
  ))),
  document.getElementById("app"),
);

Events

Synthetic events subsystem is using its own two-phase event dispatching algorithm. Custom event dispatching makes it possible to decouple event handlers from DOM elements and improve composition model.

function Events(events: EventHandler, children: Op): Op<EventsData>;

type EventHandler = EventHandlerNode | EventHandlerArray | null;
interface EventHandlerArray extends Readonly<Array<EventHandler>> { }

Events() operation is used to attach event handlers. events argument can be a singular event handler, null or recursive array of event handlers.

{ console.log("click"); }), button(_, _, "Click Me"), ), document.getElementById("app")!, );">
import { _, Events, onClick, render } from "ivi";
import { button } from "ivi-html";

render(
  Events(onClick((ev, currentTarget) => { console.log("click"); }),
    button(_, _, "Click Me"),
  ),
  document.getElementById("app")!,
);
Stop Propagation

Event handler should return true value to stop event propagation.

onClick((ev) => true);

Context

interface ContextDescriptor<T> {
  get(): T;
  set(value: T, children: Op): ContextOp<T>;
}
function contextValue<T>(): ContextDescriptor<T>;

contextValue() creates context getter get() and operation factory for context nodes set().

(); const C = statelessComponent(() => Value.get()); render( Value.set("context value", C(), ), document.getElementById("app")!, );">
import { _, context, statelessComponent, render } from "ivi";
import { div } from "ivi-html";

const Value = context<string>();
const C = statelessComponent(() => Value.get());

render(
  Value.set("context value",
    C(),
  ),
  document.getElementById("app")!,
);

TrackByKey

function TrackByKey(items: Key<any, Op>[]): OpNode<Key<any, Op>[]>;

TrackByKey() operation is used for dynamic children lists.

key(i, span(_, _, i)))), ), document.getElementById("app")!, );">
import { _, TrackByKey, key, render } from "ivi";
import { div, span } from "ivi-html";

const items = [1, 2, 3];

render(
  div(_, _,
    TrackByKey(items.map((i) => key(i, span(_, _, i)))),
  ),
  document.getElementById("app")!,
);

Attribute Directives

By default, reconciliation algorithm assigns all attributes with setAttribute() and removes them with removeAttribute() functions, but sometimes we need to assign properties or assign attributes from different namespaces. To solve this problems, ivi introduces the concept of Attribute Directives, this directives can extend the default behavior of the attributes reconciliation algorithm. It significantly reduces code complexity, because we no longer need to bake in all this edge cases into reconciliation algorithm. Also it gives an additional escape hatch to manipulate DOM elements directly.

There are several attribute directives defined in ivi packages:

// ivi
function PROPERTY<T>(v: T): AttributeDirective<T>;
function UNSAFE_HTML(v: string): AttributeDirective<string>;
function AUTOFOCUS(v: boolean): AttributeDirective<boolean>;

// ivi-html
function VALUE(v: string | number): AttributeDirective<string | number>;
function CONTENT(v: string | number): AttributeDirective<string | number>;
function CHECKED(v: boolean): AttributeDirective<boolean>;

// ivi-svg
function XML_ATTR(v: string | number | boolean): AttributeDirective<string | number>;
function XLINK_ATTR(v: string | number | boolean): AttributeDirective<string | number>;

PROPERTY() function creates an AttributeDirective that assigns a property to a property name derived from the key of the attribute.

UNSAFE_HTML() function creates an AttributeDirective that assigns an innerHTML property to an Element.

AUTOFOCUS() function creates an AttributeDirective that triggers focus when value is updated from undefined or false to true.

VALUE() function creates an AttributeDirective that assigns a value property to an HTMLInputElement or HTMLTextAreaElement.

CHECKED() function creates an AttributeDirective that assigns a checked property to an HTMLInputElement.

XML_ATTR() function creates an AttributeDirective that assigns an attribute from XML namespace, attribute name is derived from the key.

XLINK_ATTR() function creates an AttributeDirective that assigns an attribute from XLINK namespace, attribute name is derived from the key.

Example
import { input, CHECKED } from "ivi-html";

const e = input("", { type: "checked", checked: CHECKED(true) })
Custom Attribute Directives
interface AttributeDirective<P> {
  v: P;
  u?: (element: Element, key: string, prev: P | undefined, next: P | undefined) => void;
  s?: (key: string, next: P) => void;
}
function updateCustomValue(element: Element, key: string, prev: number | undefined, next: number | undefined) {
  if (prev !== next && next !== void 0) {
    (element as any)._custom = next;
  }
}

First thing that we need to do is create an update function. Update function has 4 arguments: element will contain a target DOM element, key is an attribute name that was used to assign this value, prev is a previous value and next is the current value.

In this function we are just checking that the value is changed, and if it is changed, we are assigning it to the _custom property.

export const CUSTOM_VALUE = (v: number): AttributeDirective<number> => ({ v, u: updateCustomValue });

Now we need to create a function that will be used to instantiate AttributeDirective objects.

Additional functions

Trigger an update
function requestDirtyCheck();

requestDirtyCheck() function requests a dirty checking.

Rendering virtual DOM into a document
function render(children: Op, container: Element): void;

render() function assigns a new virtual DOM root node to the container and requests dirty checking.

Components

Virtual DOM node factory

function component(
  c: (c: Component<undefined>) => () => Op,
): () => OpNode<undefined>;

function component<P1>(
  c: (c: Component) => (p1: P1, p2: P2) => Op,
  areEqual1?: undefined extends P1 ? undefined : (prev: P1, next: P1) => boolean,
  areEqual2?: undefined extends P2 ? undefined : (prev: P2, next: P2) => boolean,
): undefined extends P1 ?
  (undefined extends P2 ? (p1?: P1, p2?: P2) => ComponentOp<P1, P2> : (p1: P1, p2: P2) => ComponentOp<P1, P2>) :
  (undefined extends P2 ? (p1?: P1, p2?: P2) => ComponentOp<P1, P2> : (p1: P1, p2: P2) => ComponentOp<P1, P2>);

component() function creates a factory function that will instantiate component nodes. Factory function can have up to two properties P1 and P2.

By default, all components and hooks are using strict equality === operator as areEqual function.

(() => (attrs, children) => ( button("button", attrs, children) )); render( Button(_, "click me", ), container, );">
import { _, component, Op, render } from "ivi";
import { button } from "ivi-html";

const Button = component<{ disabled?: boolean }, Op>(() => (attrs, children) => (
  button("button", attrs, children)
));

render(
  Button(_,
    "click me",
  ),
  container,
);

Hooks

useEffect()
function useEffect<P>(
  c: Component,
  hook: (props: P) => (() => void) | void,
  areEqual?: (prev: P, next: P) => boolean,
): (props: P) => void;

useEffect() lets you perform side effects. It is fully deterministic and executes immediately when function created by useEffect() is invoked. It is safe to perform any subscriptions in useEffect() without losing any events.

const Timer = component<number>((c) => {
  let i = 0;
  const tick = useEffect<number>(c, (interval) => {
    const id = setInterval(() => {
      i++;
      invalidate(c);
    });
    return () => { clearInterval(id); };
  });

  return (t) => (
    tick(t),

    div(_, _, i),
  );
})
useMutationEffect()
function useMutationEffect<P>(
  c: Component,
  hook: (props: P) => (() => void) | void,
  areEqual?: (prev: P, next: P) => boolean,
): (props: P) => void;

useMutationEffect() lets you perform DOM mutation side effects. It will schedule DOM mutation task that will be executed immediately after all DOM updates.

useLayoutEffect()
function useLayoutEffect<P>(
  c: Component,
  hook: (props: P) => (() => void) | void,
  areEqual?: (prev: P, next: P) => boolean,
): (props: P) => void;

useLayoutEffect() lets you perform DOM layout side effects. It will schedule DOM layout task that will be executed after all DOM updates and mutation effects.

useUnmount()
function useUnmount(c: Component, hook: (token: UnmountToken) => void): void;

useUnmount() creates a hook that will be invoked when component is unmounted from the document.

hook function always receives UNMOUNT_TOKEN as a first argument, it can be used in micro optimizations to reduce memory allocations.

const C = component((c) => {
  const h = (p) => {
    if (p === UNMOUNT_TOKEN) {
      // unmount
    } else {
      // render
    }
  };
  useUnmount(c, h);
  return h;
});

Additional Functions

invalidate()
function invalidate(c: Component): void;

invalidate() marks component as dirty and requests dirty checking.

Using a Custom Hook

function useFriendStatus(c) {
  let isOnline = null;

  function handleStatusChange(status) {
    isOnline = status.isOnline;
    invalidate(c);
  }

  const subscribe = useEffect(c, (friendID) => (
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange),
    () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }
  ));

  return (friendID) => {
    subscribe(friendID);
    return isOnline;
  };
}

const FriendStatus = component((c) => {
  const getFriendStatus = useFriendStatus(c);

  return (props) => {
    const isOnline = getFriendStatus(props.friend.id);

    if (isOnline === null) {
      return "Loading...";
    }
    return isOnline ? "Online" : "Offline";
  };
});
Pass Information Between Hooks
key(e.id, EntryField(e)))), ) ); });">
const useFilter = selector(() => query().filter());
const useEntriesByFilterType = selector((filter) => (query().entriesByFilterType(filter).result));

const EntryList = component((c) => {
  const getFilter = useFilter(c);
  const getEntriesByFilterType = useEntriesByFilterType(c);

  return () => (
    ul("", { id: "todo-list" },
      TrackByKey(getEntriesByFilterType(getFilter()).map((e) => key(e.id, EntryField(e)))),
    )
  );
});

Accessing DOM Nodes

function getDOMNode(opState: OpState): Node | null;

getDOMNode() finds the closest DOM Element.

{ const m = useMutationEffect(c, () => { const divElement = getDOMNode(c); divElement.className = "abc"; }); return () => (m(), div()); });">
import { component, useMutationEffect, getDOMNode } from "ivi";
import { div } from "ivi-html";

const C = component((c) => {
  const m = useMutationEffect(c, () => {
    const divElement = getDOMNode(c);
    divElement.className = "abc";
  });

  return () => (m(), div());
});

Observables and Dirty Checking

Observables in ivi are designed as a solution for coarse-grained change detection and implemented as a directed graph (pull-based) with monotonically increasing clock for change detection. Each observable value stores time of the last update and current value.

Observables can be used to store either immutable tree structures or mutable graphs. Since ivi is fully deterministic, there isn't any value in using immutable data structures everywhere, it is better to use immutable values for small objects and mutable data structures for collections, indexing and references to big objects.

Observable

interface Observable<T> {
  t: number;
  v: T;
}
type ObservableValue<T> = T extends Observable<infer U> ? U : never;
const value = observable(1);

observable() creates an observable value.

const value = observable(1);
assign(value, 2);

assign() assigns a new value.

const value = observable({ a: 1 });
mut(value).a = 2;

mut() updates time of the last update and returns current value.

Watching observable values

const value = observable(1);
const C = statelessComponent(() => watch(value));

watch() adds observable or computed values to the list of dependencies. All dependencies are automatically removed each time component or computed value is updated.

Computeds

const a = observable(1);
const b = observable(2);
const sum = computed(() => watch(a) + watch(b));

sum();
// => 3

computed() creates computed value that will be evaluated lazily when it is requested.

Signals

Signals are observables without any values.

type Entry = ReturnType<typeof createEntry>;

const collection = observable<Entry[]>([]);
const entryPropertyChanged = signal();

function addEntry(property: number) {
  mut(collection).push(observable({ property }));
}

function entrySetProperty(entry: Entry, value: number) {
  mut(entry).property = value;
  emit(entryPropertyChanged);
}

signal() creates a new signal.

emit() emits a signal.

Watching a subset of an Observable object

const C = component((c) => {
  const get = memo((entry) => computed((prev) => {
    const v = watch(entry);
    return prev !== void 0 && prev === v.value ? prev : v.value;
  }));
  return (entry) => watch(get(entry))().value;
});

Computeds are using strict equality === as an additional change detection check. And we can use it to prevent unnecessary computations when result value is the same.

Portals

Portals are implemented in the ivi-portal package. It has a simple API:

export interface Portal {
  readonly root: Op;
  readonly entry: (children: Op) => Op;
}
function portal(rootDecorator?: (children: Op) => Op): Portal;

portal() function creates a Portal instance that has a root node and an entry() function. root node is used to render a portal root and entry() function renders elements inside of a portal.

rootDecorator argument can be used to provide a decorator for a root node, by default it is a simple identity function (v) => v.

Example

{ let showModal = false; const showEvent = onClick(() => { showModal = true; invalidate(c); }); return () => ([ showModal ? MODAL.entry(div("modal", _, "This is being rendered inside the #modal-root div.")) : null, Events(showEvent, button(_, _, "Show modal")), ]); }); render(App(), document.getElementById("app")); render(MODAL.root, document.getElementById("modal-root"));">
import { _, render, component, invalidate, Events, onClick, } from "ivi";
import { div, button } from "ivi-html";
import { portal } from "ivi-portal";

const MODAL = portal();

const App = component((c) => {
  let showModal = false;
  const showEvent = onClick(() => { showModal = true; invalidate(c); });

  return () => ([
    showModal ? MODAL.entry(div("modal", _, "This is being rendered inside the #modal-root div.")) : null,
    Events(showEvent, button(_, _, "Show modal")),
  ]);
});

render(App(), document.getElementById("app"));
render(MODAL.root, document.getElementById("modal-root"));

Environment Variables

NODE_ENV

  • production - Disables runtime checks that improve development experience.

IVI_TARGET

  • browser - Default target.
  • evergreen - Evergreen browsers.
  • electron - Electron.

Webpack Configuration

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      "process.env.IVI_TARGET": JSON.stringify("browser"),
    }),
  ],
}

Rollup Configuration

export default {
  plugins: [
    replace({
      values: {
        "process.env.NODE_ENV": JSON.stringify("production"),
        "process.env.IVI_TARGET": JSON.stringify("browser"),
      },
    }),
  ],
};

Caveats

Legacy Browsers Support

React is probably the only library that tries hard to hide all browser quirks for public APIs, almost all other libraries claim support for legacy browsers, but what it usually means is that their test suite passes in legacy browsers and their test suites doesn't contain tests for edge cases in older browsers. ivi isn't any different from many other libraries, it fixes some hard issues, but it doesn't try to fix all quirks for legacy browsers.

Component Factories Ambiguity

Stateless components implemented as immediately executed functions won't have any nodes in a "Virtual DOM" tree and reconciliation algorithm won't be able to detect when we are rendering completely different components.

const A = () => div();
const B = () => div();
render(
  condition ? A() : B(),
  container,
);

In this example, when condition is changed, instead of completely destroying previous

element and instantiating a new one, reconciliation algorithm will reuse
element.

Rendering into

Rendering into is disabled to prevent some issues. If someone submits a good explanation why this limitation should be removed, it is possible to remove this limitation from the code base.

Rendering into external Document

Rendering into external Document (iframe, window, etc) isn't supported.

Server-Side Rendering

There is no SSR.

Synthetic Events

Synthetic events subsystem dispatches events by traversing state tree. Worst case scenario is that it will need to traverse entire state tree to deliver an event, but it isn't the problem because it is hard to imagine an application implemented as a huge flat list of DOM nodes.

All global event listeners for synthetic events are automatically registered when javascript is loaded. ivi is relying on dead code elimination to prevent registration of unused event listeners. React applications has lazy event listeners registration and all global event listeners always stay registered even when they aren't used anymore, it seems that there aren't many issues with it, but if there is a good explanation why it shouldn't behave this way, it is possible to add support for removing global event listeners by using dependency counters.

There are no onMouseEnter() and onMouseLeave() events, here is an example how to implement the same behavior using onMouseOver() event.

onTouchEnd(), onTouchMove(), onTouchStart() and onWheel() are passive event listeners. onActiveTouchEnd(), onActiveTouchMove(), onActiveTouchStart() and onActiveWheel() will add active event listeners.

Dirty Checking

Dirty checking provides a solution for a wide range of edge cases. Dirty checking always goes through entire state tree, checks invalidated components, selectors, observables, propagates context values and makes it possible to efficiently solve all edge cases with nested keyed lists, fragments, holes (null ops).

Dirty checking is heavily optimized and doesn't allocate any memory. To understand how much overhead to expect from dirty checking algorithm, we can play with a stress test benchmark for dirty checking: https://localvoid.github.io/ivi-examples/benchmarks/dirtycheck/ (all memory allocations are from perf-monitor counter)

This benchmark has a tree structure with 10 root containers, each container has 10 subcontainers and each subcontainer has 50 leaf nodes. Containers are DOM elements wrapped in a stateful component node with children nodes wrapped in TrackByKey() operation, each leaf node is a DOM element wrapped in a stateful component node with text child node wrapped in a fragment and Events() operation, also each leaf node watches two observable values. It creates so many unnecessary layers to get a better understanding how it will behave in the worst case scenarios.

Unhandled Exceptions

ivi doesn't try to recover from unhandled exceptions raised from user space code. If there is an unhandled exception, it means that there is a bug in the code and bugs lead to security issues. To catch unhandled execptions, all entry points are wrapped with catchError() decorator. When unhandled exception reaches this decorator, application goes into error mode. In error mode, all entry points are blocked because it is impossible to correctly recover from bugs.

addErrorHandler() adds an error handler that will be invoked when application goes into error mode.

Portals

Portal implementation relies on the reconciler execution order.

Reconciler always mounts and updates nodes from right to left and this example won't work correctly:

render([App(), PORTAL.root], document.getElementById("app"));

To fix this example, we should either place portal roots before components that use them:

render([PORTAL.root, App()], document.getElementById("app"));

Or render them in a different container:

render(App(), document.getElementById("app"));
render(PORTAL.root, document.getElementById("portal"));

Root nodes are always updated in the order in which they were originally mounted into the document.

Custom Elements (Web Components)

Creating custom elements isn't supported, but there shouldn't be any problems with using custom elements.

Examples and demo applications

CodeSandbox

Apps

Benchmarks

License

MIT

Comments
  • package.json `main` doesn't point to a commonjs module

    package.json `main` doesn't point to a commonjs module

    It's my first run at trying out ivi with node and since node doesn't support import/export statements it throws an exception:

    /myproject/node_modules/ivi/dist/js/ivi.js:7
    export { DEV_MODE, setDevModeFlags, printError, printWarn } from "./dev_mode/dev_mode";
    ^^^^^^
    SyntaxError: Unexpected token export
    

    Seeing that both webpack and rollup look for the key module first before looking at main in package.json. We could easily get the best of both worlds.

    opened by marvinhagemeister 10
  • [Proof Of Concept] JSX/TSX Support

    [Proof Of Concept] JSX/TSX Support

    I went to write something like this library just a few weeks ago, love that I found it instead!

    I'd really like to see TSX support to make the library a little bit more intuitive for people use to the react way of doing things.

    It would be cool to see a new submodule, something like "ivi-jsx" that handles the transition but makes it easy to exclude if people would prefer not to use it.

    I threw together this quick proof of concept file:

    import * as ivi from "ivi";
    import * as html from "ivi-html";
    
    export interface ElementProps {
      className?: string;
      style?: ivi.HTMLStyleElementAttrs;
      children: ivi.OpNode<ivi.ElementData<any>>[] | ivi.OpNode<ivi.ElementData<any>>
    }
    
    export function createElement<Props extends ElementProps>(name:string|((props: Props) => ivi.OpNode<any>), props?: Props, ...children: ivi.OpNode<ivi.ElementData<Props>>[]): ivi.OpNode<ivi.ElementData<Props>> {
        if (typeof name === "string") {
          if (!html[name]) {
            throw new Error(`HTML element "${name}" not found!`);
          }
          const thisProps = props || {} as Props;
          const thisClass = thisProps["className"];
          delete thisProps.className;
          return html[name](thisClass || ivi._, thisProps, children || ivi._);
        } else {
          props.children = children;
          return name(props);
        }
    }
    
    export function createComponent<Props, State>(render: (state: ivi.OpState<ivi.ComponentHooks<State>>) => (props: Props) => ivi.OpNode<ivi.ElementData<Props>> | ivi.OpNode<ivi.ElementData<Props>>[]) {
      return ivi.component(render);
    }
    
    export function TrackByKey<T>(props: {items: T[], render: (item: T) => [any, ivi.OpNode|ivi.OpNode[]]}) {
      return ivi.TrackByKey(props.items.map(p => {
        const [key, element] = props.render(p);
        return ivi.key(key, element);
      }));
    }
    

    In addition to the file above, you'll need to drop this into the tsconfig:

            "jsxFactory": "createElement",
            "jsx": "react",
    

    Now the blog example:

    import { createComponent, createElement, TrackByKey } from "ivi-jsx";
    import * as ivi from "ivi";
    
    const Blog = createComponent((c) => {
      return (props: {posts: any[]}) => {
        return [
          <TrackByKey items={props.posts} render={(post) => {
            return [post.id, <h2>{post.title}</h2>]
          }} />,
          <hr />,
          <TrackByKey items={props.posts} render={(post) => {
            return [post.id, [<h3>{post.title}</h3>, <p>{post.content}</p>]]
          }} />
        ];
      }
    });
    
    const posts = [
      { id: 1, title: "Hello World", content: "Welcome to learning ivi!" },
      { id: 2, title: "Installation", content: "You can install ivi from npm." }
    ];
    
    ivi.render(<Blog posts={posts} />, document.getElementById("app")!);
    

    So far the only weird part I can see is the components must have their children passed down inside the props. So you'd have to either adjust the library internals or do components like this:

    const Container = createComponent((c) => {
        return (props) => {
            return <div className="test">{props.children}</div>
        }
    });
    
    ivi.render(<Container>Hello<Container/>, document.getElementById("app")!);
    
    // kicks out: <div class="test">Hello</div>
    

    I haven't dug super deep into the implications of doing things like this, the prototype I hacked together "seems" to work but I'd really like to get your input on things.

    I'd be more than happy to put together a more thought out PR if this looks like a good direction.

    opened by only-cliches 6
  • Is there any optimizations about vdom.type?

    Is there any optimizations about vdom.type?

    if (oldVdom.type !== newVdom.type){ // here
      mount(newVdom)
      unmount(oldVdom)
    }else{
     diff(oldVdom, newVdom)
    }
    

    The names of variables are so strange that I can't find them.

    opened by yisar 4
  • stopDirtyChecking

    stopDirtyChecking

    hi there

    i've just discovered ivi and i have a somewhat basic question: i'm creating custom-elements and the data and changehandling of that data which gets rendered is handled by the custom-element (v1) itself. does it mean i should always use the stopDirtChecking as found here:

    const Row = statelessComponent(({ id, label, selected }) => (
      stopDirtyChecking(
        tr(selected ? "danger" : "").c(
          td("col-md-1").c(id),
          td("col-md-4").c(a().c(label)),
          td("col-md-1").c(a().c(GlyphIcon("glyphicon glyphicon-remove delete"))),
          td("col-md-6"),
        ),
      )
    ));
    

    or is there a way to globally disable it? thanks for any enlightment

    opened by fopsdev 3
  • Trouble bundling ivi with webpack

    Trouble bundling ivi with webpack

    I'm having trouble bundling ivi with wepback and the stack trace isn't that meaningful. This even happens with a simple app like this:

    import { render } from "ivi";
    import * as h from "ivi-html";
    
    render(h.div().children("Hello World"), document.getElementById("root"));
    

    Stacktrace:

    bundle.js:5015 Uncaught Error: Cannot find module "."
        at webpackMissingModule (bundle.js:5015)
        at Object.SVG_NAMESPACE (bundle.js:5015)
        at __webpack_require__ (bundle.js:20)
        at Object.webpackEmptyContext.keys (bundle.js:12009)
        at __webpack_require__ (bundle.js:20)
        at Object.<anonymous> (bundle.js:6461)
        at __webpack_require__ (bundle.js:20)
        at module.exports.ctor.super_ (bundle.js:63)
        at bundle.js:66
    

    I'm a bit confused as to what is actually happening here. Webpack seems to stumble on the render import. A repo to easily reproduce the bug is here: https://github.com/marvinhagemeister/ivi-webpack-problem

    opened by marvinhagemeister 3
  • Question: using addEventListener instead of Events?

    Question: using addEventListener instead of Events?

    Hi localvoid,

    Is there any downside of using addEventListener instead of Events that i need to know? Because I want to use htm with ivi and Web components.

    Thanks, threeid

    opened by threeid 2
  • Any working SSR example

    Any working SSR example

    I tried to render app as html string, but it seems the server-side rendering not fully supported at this moment.

    (global as any).__IVI_DEBUG__ = true;
    (global as any).__IVI_TARGET__ = "ssr";
    
    import {writeFileSync} from 'fs';
    import {renderToString} from 'ivi';
    import {main} from './main';
    
    writeFileSync('dist/client.html', renderToString(main()));
    

    I get an error which means that it still requires browser environment despite of actual __IVI_TARGET__ value:

    node_modules/ivi/src/dom/feature_detection.ts:38
      KeyboardEvent.prototype.hasOwnProperty("key")
      ^
    ReferenceError: KeyboardEvent is not defined
    

    The KeyboardEvent proto usually defined by DOM API which is missing in Node or any other server environment.

    Of course, I can import KeyboardEvent from jsdom package or create myself, but I think this is completely wrong way.

    opened by katyo 2
  • Documentation of ivi-html

    Documentation of ivi-html

    Hello there again,

    I am trying to move the following application to ivi. I have it already in inferno, nerv, and react. Trying to make it work with ivi but I am encountering some issues probably linked to undocumented behaviour of element factories. I already solved an issue related to the fact that the first argument of the element factory (say div) is a list of classes (or so it seems). So ".class" should become "class" without the dot. Attributes seem to be attributes, and hopefully there is no funky edge case. But I have this which I can't work around

    reconciler.ts:1178 Uncaught TypeError: next.u is not a function
        at _updateAttr (reconciler.ts:1178)
        at _updateAttrs (reconciler.ts:1235)
        at _createElement (reconciler.ts:316)
        at _mountObject (reconciler.ts:365)
        at _mount (reconciler.ts:426)
        at _mountFragment (reconciler.ts:410)
        at _mount (reconciler.ts:424)
        at _mountObject (reconciler.ts:371)
        at _mount (reconciler.ts:426)
        at _update (reconciler.ts:476)
        at dirtyCheck (root.ts:47)
        at time (index.ts:177)
        at index.ts:107
        at error.ts:24
    

    This is the next value, which indeed features no u properties :

    {
      "t": {
        "f": 2,
        "d": "div"
      },
      "d": {
        "n": "App__view uk-margin-top-small uk-margin-left uk-margin-right",
        "a": {
          "data-page": "home"
        },
        "c": [{
          "t": { "f": 2, "d": "div" },
          "d": {
            "n": "HomePage",
            "a": [{ "t": { "f": 2, "d": "h1" }, "d": { "n": ["TMDb UI – Home"], "c": null } }, {
              "t": {
                "f": 2,
                "d": "legend"
              }, "d": { "n": "uk-legend", "a": { "data-testid": "PROMPT_TESTID" }, "c": ["Search for a Title:"] }
            }, {
              "t": { "f": 2, "d": "div" },
              "d": {
                "n": "SearchBar uk-inline uk-margin-bottom",
                "a": [{
                  "t": { "f": 2, "d": "a" },
                  "d": { "n": "uk-form-icon uk-form-icon-flip js-clear", "a": { "uk-icon": "icon:search" }, "c": null }
                }, {
                  "t": { "f": 2, "d": "input" },
                  "d": {
                    "n": "SearchBar__input uk-input js-input",
                    "a": { "type": "text", "value": "", "data-testid": "QUERY_FIELD_TESTID" },
                    "c": null
                  }
                }],
                "c": null
              }
            }, {
              "t": { "f": 2, "d": "h3" },
              "d": {
                "n": "uk-heading-bullet uk-margin-remove-top",
                "a": { "data-testid": "RESULTS_HEADER_TESTID" },
                "c": ["Popular Now"]
              }
            }, {
              "t": { "f": 2, "d": "div" },
              "d": {
                "n": "ResultsContainer",
                "a": { "data-testid": "RESULTS_CONTAINER_TESTID" },
                "c": [{ "t": { "f": 2, "d": "div" }, "d": { "n": ["Loading..."], "c": null } }]
              }
            }],
            "c": null
          }
        }]
      }
    }
    

    Any idea what I am doing wrong? I assume, that this may be because my first render is on an element (#app) while the subsequent renders are on the first child of that element. Do render in ivi have to be always on the same element? Could also be that the problem is that I am using webcomponent and custom tags : const movieSearch = htmlElementFactory("movie-search");?

    I have a codesandbox but the error is swallowed there : https://codesandbox.io/s/0y56rx6zkl Else, the repo corresponding to the sandbox is here : https://github.com/brucou/movie-search-app-ivi

    opened by brucou 2
  • Possible bug in VNode Class (children method)

    Possible bug in VNode Class (children method)

    The method is declared without body (L194) then declared again with body but without the parameter types: (L195). In addition, the JSDoc annotation used by the Closure Compiler is bound to the bodiless method and not compiled to JS. This may be the cause of Closure Compiler not being able to rename 'children' calls.

    opened by Enrainn 2
  • Bump node-fetch from 2.6.0 to 2.6.1

    Bump node-fetch from 2.6.0 to 2.6.1

    Bumps node-fetch from 2.6.0 to 2.6.1.

    Release notes

    Sourced from node-fetch's releases.

    v2.6.1

    This is an important security release. It is strongly recommended to update as soon as possible.

    See CHANGELOG for details.

    Changelog

    Sourced from node-fetch's changelog.

    v2.6.1

    This is an important security release. It is strongly recommended to update as soon as possible.

    • Fix: honor the size option after following a redirect.
    Commits
    • b5e2e41 update version number
    • 2358a6c Honor the size option after following a redirect and revert data uri support
    • 8c197f8 docs: Fix typos and grammatical errors in README.md (#686)
    • 1e99050 fix: Change error message thrown with redirect mode set to error (#653)
    • 244e6f6 docs: Show backers in README
    • 6a5d192 fix: Properly parse meta tag when parameters are reversed (#682)
    • 47a24a0 chore: Add opencollective badge
    • 7b13662 chore: Add funding link
    • 5535c2e fix: Check for global.fetch before binding it (#674)
    • 1d5778a docs: Add Discord badge
    • Additional commits viewable in compare view
    Maintainer changes

    This version was pushed to npm by akepinski, a new releaser for node-fetch since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Bump codecov from 3.6.5 to 3.7.1

    Bump codecov from 3.6.5 to 3.7.1

    Bumps codecov from 3.6.5 to 3.7.1.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 1
  • Bump qs from 6.5.2 to 6.5.3

    Bump qs from 6.5.2 to 6.5.3

    Bumps qs from 6.5.2 to 6.5.3.

    Changelog

    Sourced from qs's changelog.

    6.5.3

    • [Fix] parse: ignore __proto__ keys (#428)
    • [Fix] utils.merge: avoid a crash with a null target and a truthy non-array source
    • [Fix] correctly parse nested arrays
    • [Fix] stringify: fix a crash with strictNullHandling and a custom filter/serializeDate (#279)
    • [Fix] utils: merge: fix crash when source is a truthy primitive & no options are provided
    • [Fix] when parseArrays is false, properly handle keys ending in []
    • [Fix] fix for an impossible situation: when the formatter is called with a non-string value
    • [Fix] utils.merge: avoid a crash with a null target and an array source
    • [Refactor] utils: reduce observable [[Get]]s
    • [Refactor] use cached Array.isArray
    • [Refactor] stringify: Avoid arr = arr.concat(...), push to the existing instance (#269)
    • [Refactor] parse: only need to reassign the var once
    • [Robustness] stringify: avoid relying on a global undefined (#427)
    • [readme] remove travis badge; add github actions/codecov badges; update URLs
    • [Docs] Clean up license text so it’s properly detected as BSD-3-Clause
    • [Docs] Clarify the need for "arrayLimit" option
    • [meta] fix README.md (#399)
    • [meta] add FUNDING.yml
    • [actions] backport actions from main
    • [Tests] always use String(x) over x.toString()
    • [Tests] remove nonexistent tape option
    • [Dev Deps] backport from main
    Commits
    • 298bfa5 v6.5.3
    • ed0f5dc [Fix] parse: ignore __proto__ keys (#428)
    • 691e739 [Robustness] stringify: avoid relying on a global undefined (#427)
    • 1072d57 [readme] remove travis badge; add github actions/codecov badges; update URLs
    • 12ac1c4 [meta] fix README.md (#399)
    • 0338716 [actions] backport actions from main
    • 5639c20 Clean up license text so it’s properly detected as BSD-3-Clause
    • 51b8a0b add FUNDING.yml
    • 45f6759 [Fix] fix for an impossible situation: when the formatter is called with a no...
    • f814a7f [Dev Deps] backport from main
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump decode-uri-component from 0.2.0 to 0.2.2

    Bump decode-uri-component from 0.2.0 to 0.2.2

    Bumps decode-uri-component from 0.2.0 to 0.2.2.

    Release notes

    Sourced from decode-uri-component's releases.

    v0.2.2

    • Prevent overwriting previously decoded tokens 980e0bf

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.1...v0.2.2

    v0.2.1

    • Switch to GitHub workflows 76abc93
    • Fix issue where decode throws - fixes #6 746ca5d
    • Update license (#1) 486d7e2
    • Tidelift tasks a650457
    • Meta tweaks 66e1c28

    https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.1

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump codecov from 3.6.5 to 3.8.3

    Bump codecov from 3.6.5 to 3.8.3

    Bumps codecov from 3.6.5 to 3.8.3.

    Release notes

    Sourced from codecov's releases.

    v3.8.3

    Fixes

    • #329 fix: Test if response has two lines

    Dependencies

    • #306 Bump eslint-config-prettier from 7.2.0 to 8.3.0
    • #305 Bump eslint from 7.21.0 to 7.25.0
    • #302 Bump mock-fs from 4.13.0 to 4.14.0
    • #308 Bump lodash from 4.17.19 to 4.17.21
    • #309 Bump ignore-walk from 3.0.3 to 3.0.4
    • #310 Bump hosted-git-info from 2.8.8 to 2.8.9
    • #325 Bump prettier from 2.2.1 to 2.3.2
    • #326 Bump actions/setup-node from 2.1.5 to 2.2.0
    • #328 Bump lint-staged from 10.5.4 to 11.0.1
    • #330 Bump eslint from 7.25.0 to 7.31.0
    • #331 Bump ws from 7.3.1 to 7.5.3
    • #332 Bump urlgrey from 0.4.4 to 1.0.0
    • #334 Bump husky from 6.0.0 to 7.0.1
    • #333 Bump teeny-request from 7.0.1 to 7.1.1

    v3.8.2

    3.8.2

    Fixes

    • #304 Add coverage-final.json as a possible coverage file during file lookup

    v3.8.1

    Fixes

    • #246 Revert "Bump teeny-request from 6.0.1 to 7.0.0"

    v3.8.0

    Features

    • #160 Add Github Actions support

    Fixes

    • #173 Fix broken gcov command
    • #195 Update Node testing versions
    • #200 Remove flaky tests
    • #204 Create CHANGELOG and remove flaky v4 test
    • #208 Add license scan report and status
    • #220 Remove errant bitly

    Dependencies

    • #189 Bump lint-staged from 10.0.7 to 10.2.11
    • #190 [Security] Bump handlebars from 4.5.3 to 4.7.6
    • #191 Bump prettier from 1.19.1 to 2.0.5
    • #192 Bump mock-fs from 4.10.4 to 4.12.0
    • #196 Bump teeny-request from 6.0.1 to 7.0.0

    ... (truncated)

    Changelog

    Sourced from codecov's changelog.

    3.8.3

    Fixes

    • #329 fix: Test if response has two lines

    Dependencies

    • #306 Bump eslint-config-prettier from 7.2.0 to 8.3.0
    • #305 Bump eslint from 7.21.0 to 7.25.0
    • #302 Bump mock-fs from 4.13.0 to 4.14.0
    • #308 Bump lodash from 4.17.19 to 4.17.21
    • #309 Bump ignore-walk from 3.0.3 to 3.0.4
    • #310 Bump hosted-git-info from 2.8.8 to 2.8.9
    • #325 Bump prettier from 2.2.1 to 2.3.2
    • #326 Bump actions/setup-node from 2.1.5 to 2.2.0
    • #328 Bump lint-staged from 10.5.4 to 11.0.1
    • #330 Bump eslint from 7.25.0 to 7.31.0
    • #331 Bump ws from 7.3.1 to 7.5.3
    • #332 Bump urlgrey from 0.4.4 to 1.0.0
    • #334 Bump husky from 6.0.0 to 7.0.1
    • #333 Bump teeny-request from 7.0.1 to 7.1.1

    3.8.2

    Fixes

    • #304 Add coverage-final.json as a possible coverage file during file lookup

    3.8.1

    Fixes

    • #246 Revert "Bump teeny-request from 6.0.1 to 7.0.0"

    3.8.0

    Features

    • #160 Add Github Actions support

    Fixes

    • #173 Fix broken gcov command
    • #195 Update Node testing versions
    • #200 Remove flaky tests
    • #204 Create CHANGELOG and remove flaky v4 test
    • #208 Add license scan report and status
    • #220 Remove errant bitly

    Dependencies

    • #189 Bump lint-staged from 10.0.7 to 10.2.11
    • #190 [Security] Bump handlebars from 4.5.3 to 4.7.6
    • #191 Bump prettier from 1.19.1 to 2.0.5

    ... (truncated)

    Commits
    • e22061b Merge pull request #335 from codecov/3.8.3
    • 981df8b 3.8.3
    • 135555c Merge pull request #333 from codecov/dependabot/npm_and_yarn/teeny-request-7.1.1
    • 65b53a3 Merge pull request #334 from codecov/dependabot/npm_and_yarn/husky-7.0.1
    • 6e4af4d Bump teeny-request from 7.0.1 to 7.1.1
    • 1149168 Merge pull request #332 from codecov/dependabot/npm_and_yarn/urlgrey-1.0.0
    • 883785c Merge pull request #331 from codecov/dependabot/npm_and_yarn/ws-7.5.3
    • 04d5ff7 Merge pull request #330 from codecov/dependabot/npm_and_yarn/eslint-7.31.0
    • e6c5bf4 Bump husky from 6.0.0 to 7.0.1
    • f781bc4 Bump ws from 7.3.1 to 7.5.3
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump jsdom from 15.2.1 to 16.5.0

    Bump jsdom from 15.2.1 to 16.5.0

    Bumps jsdom from 15.2.1 to 16.5.0.

    Release notes

    Sourced from jsdom's releases.

    Version 16.5.0

    • Added window.queueMicrotask().
    • Added window.event.
    • Added inputEvent.inputType. (diegohaz)
    • Removed ondragexit from Window and friends, per a spec update.
    • Fixed the URL of about:blank iframes. Previously it was getting set to the parent's URL. (SimonMueller)
    • Fixed the loading of subresources from the filesystem when they had non-ASCII filenames.
    • Fixed the hidden="" attribute to cause display: none per the user-agent stylesheet. (ph-fritsche)
    • Fixed the new File() constructor to no longer convert / to :, per a pending spec update.
    • Fixed mutation observer callbacks to be called with the MutationObserver instance as their this value.
    • Fixed <input type=checkbox> and <input type=radio> to be mutable even when disabled, per a spec update.
    • Fixed XMLHttpRequest to not fire a redundant final progress event if a progress event was previously fired with the same loaded value. This would usually occur with small files.
    • Fixed XMLHttpRequest to expose the Content-Length header on cross-origin responses.
    • Fixed xhr.response to return null for failures that occur during the middle of the download.
    • Fixed edge cases around passing callback functions or event handlers. (ExE-Boss)
    • Fixed edge cases around the properties of proxy-like objects such as localStorage or dataset. (ExE-Boss)
    • Fixed a potential memory leak with custom elements (although we could not figure out how to trigger it). (soncodi)

    Version 16.4.0

    • Added a not-implemented warning if you try to use the second pseudo-element argument to getComputedStyle(), unless you pass a ::part or ::slotted pseudo-element, in which case we throw an error per the spec. (ExE-Boss)
    • Improved the performance of repeated access to el.tagName, which also indirectly improves performance of selector matching and style computation. (eps1lon)
    • Fixed form.elements to respect the form="" attribute, so that it can contain non-descendant form controls. (ccwebdesign)
    • Fixed el.focus() to do nothing on disconnected elements. (eps1lon)
    • Fixed el.focus() to work on SVG elements. (zjffun)
    • Fixed removing the currently-focused element to move focus to the <body> element. (eps1lon)
    • Fixed imgEl.complete to return true for <img> elements with empty or unset src="" attributes. (strager)
    • Fixed imgEl.complete to return true if an error occurs loading the <img>, when canvas is enabled. (strager)
    • Fixed imgEl.complete to return false if the <img> element's src="" attribute is reset. (strager)
    • Fixed the valueMissing validation check for <input type="radio">. (zjffun)
    • Fixed translate="" and draggable="" attribute processing to use ASCII case-insensitivity, instead of Unicode case-insensitivity. (zjffun)

    Version 16.3.0

    • Added firing of focusin and focusout when using el.focus() and el.blur(). (trueadm)
    • Fixed elements with the contenteditable="" attribute to be considered as focusable. (jamieliu386)
    • Fixed window.NodeFilter to be per-Window, instead of shared across all Windows. (ExE-Boss)
    • Fixed edge-case behavior involving use of objects with handleEvent properties as event listeners. (ExE-Boss)
    • Fixed a second failing image load sometimes firing a load event instead of an error event, when the canvas package is installed. (strager)
    • Fixed drawing an empty canvas into another canvas. (zjffun)

    Version 16.2.2

    • Updated StyleSheetList for better spec compliance; notably it no longer inherits from Array.prototype. (ExE-Boss)
    • Fixed requestAnimationFrame() from preventing process exit. This likely regressed in v16.1.0.
    • Fixed setTimeout() to no longer leak the closures passed in to it. This likely regressed in v16.1.0. (AviVahl)
    • Fixed infinite recursion that could occur when calling click() on a <label> element, or one of its descendants.
    • Fixed getComputedStyle() to consider inline style="" attributes. (eps1lon)
    • Fixed several issues with <input type="number">'s stepUp() and stepDown() functions to be properly decimal-based, instead of floating point-based.
    • Fixed various issues where updating selectEl.value would not invalidate properties such as selectEl.selectedOptions. (ExE-Boss)
    • Fixed <input>'s src property, and <ins>/<del>'s cite property, to properly reflect as URLs.
    • Fixed window.addEventLister, window.removeEventListener, and window.dispatchEvent to properly be inherited from EventTarget, instead of being distinct functions. (ExE-Boss)
    • Fixed errors that would occur if attempting to use a DOM object, such as a custom element, as an argument to addEventListener.

    ... (truncated)

    Changelog

    Sourced from jsdom's changelog.

    16.5.0

    • Added window.queueMicrotask().
    • Added window.event.
    • Added inputEvent.inputType. (diegohaz)
    • Removed ondragexit from Window and friends, per a spec update.
    • Fixed the URL of about:blank iframes. Previously it was getting set to the parent's URL. (SimonMueller)
    • Fixed the loading of subresources from the filesystem when they had non-ASCII filenames.
    • Fixed the hidden="" attribute to cause display: none per the user-agent stylesheet. (ph-fritsche)
    • Fixed the new File() constructor to no longer convert / to :, per a pending spec update.
    • Fixed mutation observer callbacks to be called with the MutationObserver instance as their this value.
    • Fixed <input type=checkbox> and <input type=radio> to be mutable even when disabled, per a spec update.
    • Fixed XMLHttpRequest to not fire a redundant final progress event if a progress event was previously fired with the same loaded value. This would usually occur with small files.
    • Fixed XMLHttpRequest to expose the Content-Length header on cross-origin responses.
    • Fixed xhr.response to return null for failures that occur during the middle of the download.
    • Fixed edge cases around passing callback functions or event handlers. (ExE-Boss)
    • Fixed edge cases around the properties of proxy-like objects such as localStorage or dataset. (ExE-Boss)
    • Fixed a potential memory leak with custom elements (although we could not figure out how to trigger it). (soncodi)

    16.4.0

    • Added a not-implemented warning if you try to use the second pseudo-element argument to getComputedStyle(), unless you pass a ::part or ::slotted pseudo-element, in which case we throw an error per the spec. (ExE-Boss)
    • Improved the performance of repeated access to el.tagName, which also indirectly improves performance of selector matching and style computation. (eps1lon)
    • Fixed form.elements to respect the form="" attribute, so that it can contain non-descendant form controls. (ccwebdesign)
    • Fixed el.focus() to do nothing on disconnected elements. (eps1lon)
    • Fixed el.focus() to work on SVG elements. (zjffun)
    • Fixed removing the currently-focused element to move focus to the <body> element. (eps1lon)
    • Fixed imgEl.complete to return true for <img> elements with empty or unset src="" attributes. (strager)
    • Fixed imgEl.complete to return true if an error occurs loading the <img>, when canvas is enabled. (strager)
    • Fixed imgEl.complete to return false if the <img> element's src="" attribute is reset. (strager)
    • Fixed the valueMissing validation check for <input type="radio">. (zjffun)
    • Fixed translate="" and draggable="" attribute processing to use ASCII case-insensitivity, instead of Unicode case-insensitivity. (zjffun)

    16.3.0

    • Added firing of focusin and focusout when using el.focus() and el.blur(). (trueadm)
    • Fixed elements with the contenteditable="" attribute to be considered as focusable. (jamieliu386)
    • Fixed window.NodeFilter to be per-Window, instead of shared across all Windows. (ExE-Boss)
    • Fixed edge-case behavior involving use of objects with handleEvent properties as event listeners. (ExE-Boss)
    • Fixed a second failing image load sometimes firing a load event instead of an error event, when the canvas package is installed. (strager)
    • Fixed drawing an empty canvas into another canvas. (zjffun)

    16.2.2

    • Updated StyleSheetList for better spec compliance; notably it no longer inherits from Array.prototype. (ExE-Boss)
    • Fixed requestAnimationFrame() from preventing process exit. This likely regressed in v16.1.0.
    • Fixed setTimeout() to no longer leak the closures passed in to it. This likely regressed in v16.1.0. (AviVahl)
    • Fixed infinite recursion that could occur when calling click() on a <label> element, or one of its descendants.
    • Fixed getComputedStyle() to consider inline style="" attributes. (eps1lon)
    • Fixed several issues with <input type="number">'s stepUp() and stepDown() functions to be properly decimal-based, instead of floating point-based.

    ... (truncated)

    Commits
    • 2d82763 Version 16.5.0
    • 9741311 Fix loading of subresources with Unicode filenames
    • 5e46553 Use domenic's ESLint config as the base
    • 19b35da Fix the URL of about:blank iframes
    • 017568e Support inputType on InputEvent
    • 29f4fdf Upgrade dependencies
    • e2f7639 Refactor create‑event‑accessor.js to remove code duplication
    • ff69a75 Convert JSDOM to use callback functions
    • 19df6bc Update links in contributing guidelines
    • 1e34ff5 Test triage
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump ajv from 6.10.2 to 6.12.6

    Bump ajv from 6.10.2 to 6.12.6

    Bumps ajv from 6.10.2 to 6.12.6.

    Release notes

    Sourced from ajv's releases.

    v6.12.6

    Fix performance issue of "url" format.

    v6.12.5

    Fix uri scheme validation (@​ChALkeR). Fix boolean schemas with strictKeywords option (#1270)

    v6.12.4

    Fix: coercion of one-item arrays to scalar that should fail validation (failing example).

    v6.12.3

    Pass schema object to processCode function Option for strictNumbers (@​issacgerges, #1128) Fixed vulnerability related to untrusted schemas (CVE-2020-15366)

    v6.12.2

    Removed post-install script

    v6.12.1

    Docs and dependency updates

    v6.12.0

    Improved hostname validation (@​sambauers, #1143) Option keywords to add custom keywords (@​franciscomorais, #1137) Types fixes (@​boenrobot, @​MattiAstedrone) Docs:

    v6.11.0

    Time formats support two digit and colon-less variants of timezone offset (#1061 , @​cjpillsbury) Docs: RegExp related security considerations Tests: Disabled failing typescript test

    Commits
    • fe59143 6.12.6
    • d580d3e Merge pull request #1298 from ajv-validator/fix-url
    • fd36389 fix: regular expression for "url" format
    • 490e34c docs: link to v7-beta branch
    • 9cd93a1 docs: note about v7 in readme
    • 877d286 Merge pull request #1262 from b4h0-c4t/refactor-opt-object-type
    • f1c8e45 6.12.5
    • 764035e Merge branch 'ChALkeR-chalker/fix-comma'
    • 3798160 Merge branch 'chalker/fix-comma' of git://github.com/ChALkeR/ajv into ChALkeR...
    • a3c7eba Merge branch 'refactor-opt-object-type' of github.com:b4h0-c4t/ajv into refac...
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • Bump node-fetch from 2.6.0 to 2.6.7

    Bump node-fetch from 2.6.0 to 2.6.7

    Bumps node-fetch from 2.6.0 to 2.6.7.

    Release notes

    Sourced from node-fetch's releases.

    v2.6.7

    Security patch release

    Recommended to upgrade, to not leak sensitive cookie and authentication header information to 3th party host while a redirect occurred

    What's Changed

    Full Changelog: https://github.com/node-fetch/node-fetch/compare/v2.6.6...v2.6.7

    v2.6.6

    What's Changed

    Full Changelog: https://github.com/node-fetch/node-fetch/compare/v2.6.5...v2.6.6

    v2.6.2

    fixed main path in package.json

    v2.6.1

    This is an important security release. It is strongly recommended to update as soon as possible.

    See CHANGELOG for details.

    Changelog

    Sourced from node-fetch's changelog.

    Changelog

    All notable changes will be recorded here.

    The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

    What's Changed

    New Contributors

    Full Changelog: https://github.com/node-fetch/node-fetch/compare/v3.1.0...v3.1.2

    3.1.0

    What's Changed

    ... (truncated)

    Commits
    • 1ef4b56 backport of #1449 (#1453)
    • 8fe5c4e 2.x: Specify encoding as an optional peer dependency in package.json (#1310)
    • f56b0c6 fix(URL): prefer built in URL version when available and fallback to whatwg (...
    • b5417ae fix: import whatwg-url in a way compatible with ESM Node (#1303)
    • 18193c5 fix v2.6.3 that did not sending query params (#1301)
    • ace7536 fix: properly encode url with unicode characters (#1291)
    • 152214c Fix(package.json): Corrected main file path in package.json (#1274)
    • b5e2e41 update version number
    • 2358a6c Honor the size option after following a redirect and revert data uri support
    • 8c197f8 docs: Fix typos and grammatical errors in README.md (#686)
    • Additional commits viewable in compare view
    Maintainer changes

    This version was pushed to npm by endless, a new releaser for node-fetch since your current version.


    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
Releases(v1.0.1)
  • v1.0.1(May 4, 2020)

    Bug Fixes

    • Fixed getDOMNode() returning null value when first non-null node in a fragment or TrackByKey operation doesn't have any DOM nodes.
    • Fixed event dispatching algorithm visiting more nodes than it is necessary.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(Apr 3, 2020)

    Components

    Added second prop to components.

    const Button = statelessComponent<{ id: string }, Op>((props, children) => (
      button("button", props, children)
    ));
    
    Button({ id: "button-id" },
      "Click Me",
    );
    

    Custom areEqual function

    const View = statelessComponent<number, [number, number]>(
      (a, b) => (
        a + b[0] + b[1]
      ),
      undefined,
      shallowEqualArray,
    );
    View(1, [2, 3]);
    

    Dirty Checking / Observables

    Dirty checking API were redesigned to improve support for use cases with coarse-grained observable graphs and mutable data structures.

    New API for dirty checking is composable and can be used in stateless components.

    • useSelect() hook were removed.
    • Added pull-based observables.
    • Context is reimplemented with observables and it is now way much cheaper to dirty check.

    Examples

    Computed Values (lazy evaluation)
    const a = observable(1);
    const b = observable(2);
    const sum = computed(() => watch(a) + watch(b));
    const A = statelessComponent(() => div(_, _, watch(sum)()));
    
    Basic selectors with immutable state
    const STATE = { value: 1 };
    const A = component((c) => {
      const getValue = selector(() => STATE.value);
      return () => div(_, _, watch(getValue)());
    });
    
    Memoized selector with immutable state
    const STATE = { a: 1, b: 2 };
    const A = component((c) => {
      const getValue = selector((prev) => (
        prev !== void 0 && prev.a === STATE.a && prev.b === STATE.b ? prev :
          { a: STATE.a, b: STATE.b, result: STATE.a + STATE.b };
      ));
      return () => div(_, _, watch(getValue)());
    });
    
    Composition
    const a = observable(1);
    const A = component((c) => {
      const getValue = memo((i) => computed(() => watch(a) + i));
      return (i) => div(_, _, watch(getValue(i))());
    });
    

    Boolean DOM Attribute Values

    Removed automagic conversion from boolean values to empty string. Correct attribute values should be specified explicitly.

    textContent="" Optimization

    This optimization has a quite noticeable impact in popular benchmarks. But in real applications, use cases that would benefit from this optimization will work significantly faster by wrapping lists into a transient DOM node.

    Deep State Tracking

    Deep state tracking optimization were removed. It is one of those optimizations that improve performance in benchmarks, but make it worse in real applications.

    This optimization worked by updating node state flags during stack unwinding. It saved information about node subtree, so we could skip dirty checking and unmounting for subtrees that didn't have any stateful components. In applications decomposed into small components there will be many stateful components used as leaf nodes, so instead of optimizing, it will make dirty checking and reconciliation algorithms slightly slower. Also, this optimization were adding a lot of complexity to the reconciliation algorithm.

    Simplified track by key algorithm

    Instead of returning LIS indices, nodes that are part of LIS are now marked in the input array.

    Events

    Stop Propagation

    Synthetic event handlers do not propagate events anymore. To propagate events, event handler should return DispatchEvent.Propagate value.

    Move Events

    Removed touch/mouse/pointer move events. Move event handlers usually attached when down event is triggered. To make sure that we don't lose any move events, we can't wait until next frame is rerendered, so move event handlers should be attached with native DOM api.

    Server Side Rendering

    Removed. Not interested in supporting this feature.

    Source code(tar.gz)
    Source code(zip)
  • v0.27.1(May 8, 2019)

    Deep state flags propagation algorithm were redesigned to merge flags only when going through fragments and TrackByKey nodes. New algorithm also fixes some edge cases when deep state flags were kept assigned even when subtree no longer had this state.

    Source code(tar.gz)
    Source code(zip)
  • v0.27.0(May 1, 2019)

    Optimizations

    Reduced memory consumption by operation nodes. All properties are now inlined into operation nodes and there are three different operation node shapes. Almost all callsites should stay in monomorphic state, except for the one that accessing operation type in the mount and update functions, it will be in polymorphic state and it is ok as long as it doesn't transition into megamorphic state.

    Bug Fixes

    • Fixed nextNode assignment in a dirty checking algorithm when it is going through DOM elements.
    Source code(tar.gz)
    Source code(zip)
  • v0.26.0(Apr 29, 2019)

    Optimizations

    Reduced memory consumption in component hooks, refs and portals.

    API that is used for this optimizations is now public. This API is using several objects: TASK_TOKEN, SELECT_TOKEN and UNMOUNT_TOKEN to identify who is invoking a callback function. Callbacks registered with scheduleMicrotask() will receive TASK_TOKEN as a first argument, hooks added with useUnmount() will receive UNMOUNT_TOKEN. SELECT_TOKEN is used internally to optimize useSelect().

    Immediately executed useEffect()

    useEffect() hook is now immediately executed when it is invoked in the internal component "update" function. It makes it fully deterministic to prevent unexpected bugs when useEffect() is used to listen some observable value that can generate an event before microtask with an effect is executed. When it is immediately executed, it will guarantee that we don't miss any events generated by an observable value.

    Source code(tar.gz)
    Source code(zip)
  • v0.25.0(Apr 25, 2019)

    Context

    New context implementation provides a slightly different API that makes it possible to remove dirty context checking during updates and dirty checking, doesn't require to provide value type when retrieving context values, and doesn't use global namespace for context keys.

    BEFORE:

    const C = component((c) => {
      const getValue = useSelect((c) => context<{ value: number }>().value);
      return () => getValue();
    });
    
    render(
      Context({ value: 123 },
        C(),
      ),
      container,
    );
    

    AFTER:

    const Value = contextValue<number>();
    const C = component((c) => {
      const getValue = useSelect((c) => Value.get());
      return () => getValue();
    });
    
    render(
      Value.set(123,
        C(),
      ),
      container,
    );
    

    Reconciler

    Children reconciliation algorithm for fragments were changed to dynamically add/remove at the end of the fragment instead of reinstantiating entire fragment when fragment length is changed.

    Source code(tar.gz)
    Source code(zip)
  • v0.24.0(Apr 22, 2019)

    shouldUpdate

    All shouldUpdate functions are replaced with their inverse function areEqual.

    APIs affected by this change:

    • component(_, shouldUpdate)
    • statelessComponent(_, shouldUpdate)
    • useSelect(_, shouldUpdate)
    • useEffect(_, shouldUpdate)
    • useLayoutEffect(_, shouldUpdate)
    • useMutationEffect(_, shouldUpdate)
    • memo(_, shouldUpdate)
    • selector(_, shouldUpdate)

    Tests

    New package ivi-jest adds a collection of useful tools for testing with jest library. All ivi tests were completely rewritten using a new ivi-jest library.

    Scheduler

    ivi-scheduler and ivi-test-scheduler packages were removed. Old unit tests were using package aliasing to mock scheduler behavior and now it is not needed anymore.

    Events

    Removed optional bubbling, all events are now always bubble.

    Bug Fixes

    • Fast path for TrackByKey hasn't been executed correctly when nodes doesn't change their positions. It is hard to test because it is still works correctly even when this fast path doesn't execute.
    • Prevent VALUE() and CONTENT() attribute directives from accepting numbers, they should work only with string values.
    • shouldUpdate functions hasn't been executed correctly in component hooks and caused more updates than it was necessary.
    Source code(tar.gz)
    Source code(zip)
  • v0.23.0(Apr 12, 2019)

    Synthetic Events

    Synthetic event internals were heavily redesigned to reduce overall complexity and improve API flexibility for customsynthetic events.

    Custom synthetic events can now inject their own behavior into event flow of native events. It should significantly improve performance when there are many custom synthetic events as it won't be necessary to traverse virtual dom tocollect dispatch targets for each custom synthetic event.

    API for creating custom synthetic events is still an unstable API and it is most likely that there will be changes in the future, but it is an extremely useful API that solves alot of problems with UI applications.

    Native events are no longer wrapped in a SyntheticNativeEvent object

    BEFORE

    onClick((ev) => {
      console.log(ev.native.target); // target
    });
    

    AFTER:

    onClick((ev) => {
      console.log(ev.target);
    });
    

    EventFlags is removed

    To stop event propagation event handler should return true value.

    BEFORE:

    onClick((ev) => {
      return EventFlags.PreventDefault | EventFlags.StopPropagation;
    });
    

    AFTER:

    onClick((ev) => {
      ev.preventDefault();
      return true;
    });
    

    currentTarget is now accessible as a second argument

    BEFORE

    onClick((ev) => {
      console.log(ev.node); // currentTarget
    });
    

    AFTER:

    onClick((ev, currentTarget) => {
      console.log(currentTarget);
    });
    

    SyntheticEvent interface is removed

    SyntheticEvent interface had two properties: node and timestamp. node were used to assign current target, it is replaced with an additional argument in all event handler functions. timestamp is a leftover from an old synthetic events implementation that tried to fix cross-browser quirks. For many custom synthetic events this property doesn't have any value and it custom event implementor should decide when timestamp value is necessary.

    beforeNativeEvent() and afterNativeEvent() are removed

    It is replaced with an addNativeEventMiddleware().

    addNativeEventMiddleware(MOUSE_DOWN, (event, next) => {
      // beforeNativeEvent...
      next(event);
      // afterNativeEvent...
    });
    

    Portals

    Portals were completely redesigned and moved to ivi-portal package. Portals now correctly propagate context through portal entries.

    import { _, render, component, invalidate, Events, onClick, } from "ivi";
    import { div, button } from "ivi-html";
    import { portal } from "ivi-portal";
    
    const MODAL = portal();
    
    const App = component((c) => {
      let showModal = false;
      const showEvent = onClick(() => { showModal = true; invalidate(c); });
    
      return () => (
        [
          showModal ? MODAL.entry(div("modal", _, "This is being rendered inside the #modal-root div.")) : null,
          Events(showEvent,
            button(_, _, "Show modal"),
          ),
        ]
      );
    });
    
    render(App(), document.getElementById("app"));
    render(MODAL.root, document.getElementById("modal-root"));
    

    Error Handling

    Unhandled exceptions raised inside of a catchError() block are now considered as userspace bugs and will change application state to "error". When application state is "error", all entry points wrapped in catchError() will be blocked to prevent potential security issues because it is impossible to infer which part of an application state caused a bug.

    All ivi entry points like render(), synthetic event handlers, etc are wrapped with a catchError() block.

    Creating custom functions wrapped with a catchError()

    const entryFn = catchError((arg1, arg2) => {
      // ...
    });
    
    entryFn(arg1, arg2);
    

    Reconciler

    • Fixed bug when direct child node of a Context() node triggers replace operation.
    • Fixed bug when strictly equal direct child node of an HTML/SVG element doesn't trigger deep dirty checking.
    • Fixed bug when useUnmount() hook hasn't been receiving an undocumented true value as a first argument. It is an unstable feature that can be used for micro optimizations in custom hooks.
    • Added shortcuts for DOM property accesors that should reduce megamorphic call-sites.

    Misc

    • Replaced containsRelatedTarget() with a generic function containsDOMElement().
    • Added hasDOMElementChild() function.
    • Removed autofix for Mouse Event bubbling in iOS
    • Added VisitNodesDirective to get a better control over visitNodes() algorithm.
    • Added onTransitionRun() and onTransitionStart() events.
    Source code(tar.gz)
    Source code(zip)
  • v0.22.0(Mar 15, 2019)

    Added support for events:

    • onBeforeInput()
    • onTransitionCancel()
    • onTransitionEnd()

    Breaking Changes

    Global Variables replaced with Environment Variables

    __IVI_DEBUG__ and __IVI_TARGET__ were replaced with process.env.NODE_ENV !== "production" and process.env.IVI_TARGET to support parcel bundler Issue #10.

    Source code(tar.gz)
    Source code(zip)
  • v0.21.0(Feb 11, 2019)

    • Full support for server-side rendering renderToString()
    • Reduced code size

    Breaking Changes

    Global Variables

    DEBUG and TARGET were renamed to __IVI_DEBUG__ and __IVI_TARGET__ to prevent name conflicts with variables that can be used in different packages.

    useSelect()

    Context argument is removed from selectors, context() function should be used to access current context.

    function useSelect<T>(
      c: StateNode,
      selector: (props?: undefined, prev?: T | undefined) => T,
    ): () => T;
    function useSelect<T, P>(
      c: StateNode,
      selector: (props: P, prev?: T | undefined) => T,
      shouldUpdate?: undefined extends P ? undefined : (prev: P, next: P) => boolean,
    ): undefined extends P ? () => T : (props: P) => T;
    

    Attribute Directives

    Attribute directives were changed to support server-side rendering:

    interface AttributeDirective<P> {
      v: P;
      u?: (element: Element, key: string, prev: P | undefined, next: P | undefined) => void;
      s?: (key: string, next: P) => void;
    }
    

    s() method can be used to alter renderToString() behavior.

    VALUE() directive

    VALUE() directive now works only with HTMLInputElement elements. New CONTENT() directive should be used to assign value for HTMLTextArea elements.

    VALUE() directive emits value="${v}" attribute when rendered to string.

    CONTENT() directive emits children string <textarea>${v}</textarea> when rendered to string.

    Source code(tar.gz)
    Source code(zip)
  • v0.16.0(Oct 23, 2018)

  • 0.15.0(Jun 21, 2018)

    Bug Fixes

    • vdom: add missing exports (4ab29a9)

    Features

    • scheduler: add optional argument flags to render() function (6be1314)
    • scheduler: fail early when scheduler hasn't been configured (1eeb3b2)
    • scheduler: improve invalidate functions (8f5a328)
    • scheduler: make default invalidate handler NOOP (ec6a479)
    • scheduler: remove obsolete function isHidden() (23ab024)
    • state: simplify store implementation (79c31a2)
    • vdom: add support for removing events in EVENT() (437e4ce)
    • vdom: add syncable value AUTOFOCUS() (18460e7)
    • vdom: add syncable value EVENT() (2546f8e)
    • vdom: move text node factory t() from html package to ivi (d53d8e9)
    • vdom: remove direct dependency with a complex scheduler (34807fd)
    Source code(tar.gz)
    Source code(zip)
  • 0.14.0(Jun 16, 2018)

    Bug Fixes

    • events: fix incorrect imports ivi-events => events (368c387)
    • gestures: fixes browser quirk with TouchMove events (9fedf9e)
    • test: fix rendering attrs to snapshot (syncable values) (9f1de27)
    • types: support number types for css property "bottom" (4ff9486)
    • vdom: remove obsoleted checks in DEBUG mode (4274d57)

    Code Refactoring

    • vdom: rename newPropsReceived() to propsChanged() (6434f5a)

    Features

    • core: add underscore _ as an alias to undefined (35834f4)
    • debug: add DEBUG pubsub to expose internal state (466aba2)
    • gestures: add multitouch transform gesture recognizer (28991fe)
    • gestures: change gesture recognizers lifecycle (af2b86a)
    • gestures: fix bugs, add more gesture recognizers (a63b27f)
    • gestures: fully working gesture events prototype (b310dcf)
    • scheduler: add beforeUpdate / afterUpdate repeatable tasks (f599405)
    • scheduler: remove frame task queue after (d3c4f72)
    • scheduler: remove visibility observers (d816fda)
    • types: add types for specialized properties in attribute lists (69cc9a2)
    • vdom: add mapIterable() to support iterable objects (c09c5cb)
    • improve dev mode checks (4e7db28)
    • vdom: add universal syncable value PROPERTY() (111c309)
    • vdom: don't trigger updated() for all parents (5c75401)
    • vdom: new attribute syncing algorithm (564957d)
    • vdom: remove support for null nodes returned from mapIterable() (e3c88a5)
    • vdom: rename instance getters (bbcf255)
    • vdom: replace VNode methods a() and s() with factory args (4f00a52)
    • remove factories for obsolete elements, improve types for attrs (c2b9173)
    • rename INPUT_VALUE() and INPUT_CHECKED() to VALUE() and CHECKED() (943a414)

    Performance Improvements

    • events: improve event dispatcher algorithm (352287a)

    BREAKING CHANGES

    • vdom: getDOMInstanceFromVNode() and getComponentInstanceFromVNode() were renamed to getDOMNode() and getComponent()
    • vdom: VNode methods value() and unsafeHTML() were removed.

    Before:

    input("", { type: "checked" }).value(true);
    use("", { "xlink:href": "sprite.svg#a" });
    div().unsafeHTML("abc");
    

    After:

    input("", { type: "checked", checked: CHECKED(true) });
    use("", { "xlink:href": XLINK_ATTR("sprite.svg#a") });
    div("", { unsafeHTML: UNSAFE_HTML("abc") });
    
    • scheduler: currentFrameAfter() and nextFrameAfter() functions were removed.
    • scheduler: DOM reader and animation tasks were replaced with beforeUpdate() and afterUpdate() task lists.
    • vdom: Component lifecycle method newPropsReceived() renamed to propsChanged().
    • vdom: VNode methods a() and s() were replaced with optional arguments for all element factory functions.

    Before:

    div("className").a({ id: "ID" }).s({ color: "red" });
    

    After:

    div("className", { id: "ID" }, { color: "red" });
    
    • vdom: updated() lifecycle is now triggered only for components that were updated. Parent components won't be receiving any information that their children were updated.

    Initially it was implemented to solve use cases with jumping scroll positions when children layout is modified. But there is a better way, scheduler allows to register repeatable tasks that will be invoked before any rendering to read from the DOM, and after all rendering is done, this hooks solve this use case perfectly. And since I don't know about any use cases that would require such behavior, it would be better to reduce API surface. All major frameworks doesn't support such behavior.

    Source code(tar.gz)
    Source code(zip)
  • 0.13.0(May 29, 2018)

    Bug Fixes

    • events: use correct options for active events (c25e3eb)

    Features

    • events: add checks in DEBUG mode when removing event listeners (a28cde2)
    • events: completely redesigned synthetic events (a0ad90d)

    BREAKING CHANGES

    • events: ivi-events package were removed, event handler factories should be imported from ivi package.
    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(May 24, 2018)

    Code Refactoring

    • events: remove methods from synthetic events (e6d3f1e)

    Features

    • events: check returned event flags in DEBUG mode (2fc17db)

    Performance Improvements

    • gestures: optimize velocity tracker (aeba6bd)

    BREAKING CHANGES

    • events: Synthetic event methods preventDefault() and stopPropagation() were removed.

    Before:

    onClick((ev) => {
      ev.preventDefault();
      ev.stopPropagation();
    });
    

    After:

    onClick(() => EventFlags.PreventDefault | EventFlags.StopPropagation);
    
    Source code(tar.gz)
    Source code(zip)
  • 0.11.1(May 15, 2018)

  • 0.11.0(May 14, 2018)

    0.11.0 (2018-05-14)

    Bug Fixes

    • gestures: replace FEATURES with TOUCH_EVENTS (30f154e)
    • scheduler: ignore autofocusing on text nodes (71b45ae)

    Code Refactoring

    • core: split feature detection flags into several constants (23bb7d2)
    • remove specialized input and button factories (6ec699e)
    • vdom: change shouldUpdate API for stateless components (e4daf4c)
    • vdom: rename children() to fragment() (b832e67)
    • vdom: rename function component() to statefulComponent() (b591819)
    • vdom: rename function elementFactory() to element() (6324a91)

    Features

    • debug: enable HTML nesting rules violation checker (2695a8b)
    • test: add hasFactory() predicate (8abbdce)
    • test: add hasNextSibling() predicate (5d10979)
    • test: add support for <stopDirtyChecking /> nodes to snapshots (cdfa1fd)
    • vdom: check context child node type in dev mode (8c8f97f)
    • vdom: check return type for render functions in dev mode (c479ca4)
    • vdom: replace autofocus() method with function (acf3bf4)
    • vdom: use value() method to assign checked value for inputs (39c8b40)

    BREAKING CHANGES

    • vdom: function children() is renamed to fragment().
    • vdom: shouldUpdate for stateless components is now assigned with a withShouldUpdate() function.

    Before:

    const C = statelessComponent(
      (props) => h.div().c(props.title),
      (prev, next) => (prev.title !== next.title),
    );
    

    After:

    const C = withShouldUpdate(
      (prev, next) => (prev.title !== next.title),
      statelessComponent(
        (props) => h.div().c(props.title),
      ),
    );
    
    • specialized input and button factories were removed.

    Before:

    h.inputCheckbox();
    h.buttonReset();
    

    After:

    h.input().a({ type: "checkbox" });
    h.button().a({ type: "reset" });
    
    • vdom: elementFactory() function renamed to element()
    • vdom: component() function renamed to statefulComponent()
    • vdom: VNode method checked() is removed.

    Before:

    h.inputCheckbox().checked(true);
    

    After:

    h.inputCheckbox().value(true);
    
    • vdom: VNode method autofocus() is replaced with autofocus() function.

    Before:

    h.div().autofocus(true);
    

    After:

    autofocus(h.div());
    
    • core: FEATURE constant and FeatureFlags enum are replaced with:
    • PASSIVE_EVENTS
    • KEYBOARD_EVENT_KEY
    • MOUSE_EVENT_BUTTONS
    • TOUCH_EVENTS
    • POINTER_EVENTS
    • INPUT_DEVICE_CAPABILITIES
    Source code(tar.gz)
    Source code(zip)
Owner
Boris Kaul
Boris Kaul
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Absolutely minimal view layer for building web interfaces.

Superfine Superfine is a minimal view layer for building web interfaces. Think Hyperapp without the framework—no state machines, effects, or subscript

Jorge Bucaran 1.6k Dec 29, 2022
A library of high-quality primitives that help you build accessible user interfaces with SolidJS.

Solid Aria A library of high-quality primitives that help you build accessible user interfaces with SolidJS. Primitives @solid-aria/primitives - Expor

SolidJS Community 205 Jan 7, 2023
Types generator will help user to create TS types from JSON. Just paste your single object JSON the Types generator will auto-generate the interfaces for you. You can give a name for the root object

Types generator Types generator is a utility tool that will help User to create TS Interfaces from JSON. All you have to do is paste your single objec

Vineeth.TR 16 Dec 6, 2022
Million is a lightweight (<1kb) Virtual DOM. It's really fast and makes it easy to create user interfaces.

?? Watch Video ?? Read the docs ?? Join our Discord What is Million? Million is a lightweight (<1kb) Virtual DOM. It's really fast and makes it easy t

Derek Jones 5 Aug 24, 2022
Generated TypeScript interfaces for Aptos Move.

Aptos Framework TypeScript SDK Generated TypeScript interfaces for Aptos Move. This contains types for: AptosFramework MoveNursery MoveStdlib Releases

Aptosis 7 Aug 25, 2022
A monorepo for comma.ai web interfaces and packages

comma webapps This mono-repository contains the web applications and packages for the web UIs of comma.ai Contributing Just pick something and work on

null 5 Sep 27, 2022
AweSome Book App displays the book details entered by user and saves the information in Local storage. User can add and remove a book title/author to the library and from the library.

Awesome Book App with ES6 Used npm init -y command to create package.json file. Created the entry point for the JavaScript code called index.js Create

Krishna Prasad Acharya 8 Aug 15, 2022
A lightweight (<1Kb) JavaScript package to facilitate a11y-compliant tabbed interfaces

A11y Tabs A lightweight (<1Kb) JavaScript package to facilitate a11y-compliant tabbed interfaces. Documentation ↗ Demo on Codepen ↗ Features: Support

null 5 Nov 20, 2022
Remix enables you to build fantastic user experiences for the web and feel happy with the code that got you there. In this workshop, we'll look at some more advanced use cases when building Remix applications.

?? Advanced Remix Workshop Remix enables you to build fantastic user experiences for the web and feel happy with the code that got you there. In this

Frontend Masters 167 Dec 9, 2022
This project is built with JavaScript, Webpack, HTML & CSS, Leaderboard api. When user clicks on Refresh button it hits the api and responds with the data, The user can also post data to the api

leaderboad Description the project. this project is about the leaderboad i did during Microverse to build a website for adding Data to the API and fet

Emmanuel Moombe 4 May 30, 2022
Satyam Sharma 3 Jul 8, 2022
Toolkit for building scalable web applications with TypeScript, React, Redux and Apollo-Client

TsToolbox Toolkit for building scalable web applications with TypeScript, React, Redux and Apollo-Client (inspired by ReKit) ⚠ ⚠ ⚠ Work in Progress ⚠

Daniel Nikravesh 7 Apr 14, 2022
Kyrillos Hany 14 Aug 10, 2022
A-Frame Element is a simple library for building fast, lightweight web components for 3D development

aframe-element is a library inspired from the very nice library Polymer lit to map A-Frame AR / VR / 3D elements on typescript classes like Angular/React/Lit.

null 6 May 31, 2022