<|start_filename|>package.json<|end_filename|>
{
  "name": "uhooks",
  "version": "0.2.8",
  "description": "micro hooks: a minimalistic client/server hooks' implementation",
  "main": "./cjs/index.js",
  "scripts": {
    "build": "npm run cjs && npm run espruino && npm run rollup:babel && npm run rollup:es && npm run rollup:async && npm run min && npm run test && npm run size",
    "cjs": "ascjs --no-default esm cjs",
    "espruino": "node -e 'console.log(require(`fs`).readFileSync(`esm/e.js`).toString().replace(/^ *\\/\\/.*\\n/gm,``).replace(/export.*/,``).trim())'>e.js",
    "rollup:async": "rollup --config rollup/async.config.js && sed -i.bck 's/^var /self./' async.js && rm -rf async.js.bck",
    "rollup:es": "rollup --config rollup/es.config.js && sed -i.bck 's/^var /self./' es.js && rm -rf es.js.bck",
    "rollup:babel": "rollup --config rollup/babel.config.js && sed -i.bck 's/^var /self./' index.js && rm -rf index.js.bck",
    "min": "terser index.js --comments='/^!/' -c -m -o min.js",
    "coveralls": "c8 report --reporter=text-lcov | coveralls",
    "size": "echo 'async ES6' && cat async.js | brotli | wc -c && echo '' && echo 'sync ES5 / ES6' && cat min.js | brotli | wc -c && cat es.js | brotli | wc -c && echo '' && echo 'espruino' && cat e.js | brotli | wc -c",
    "test": "c8 node test/e.js && c8 node test/index.js"
  },
  "keywords": [
    "hooks",
    "lightweight",
    "minimal",
    "client",
    "server"
  ],
  "author": "<NAME>iammarchi",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/preset-env": "^7.14.7",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "@ungap/degap": "^0.2.6",
    "ascjs": "^5.0.1",
    "c8": "^7.7.3",
    "coveralls": "^3.1.0",
    "rollup": "^2.52.3",
    "rollup-plugin-includepaths": "^0.2.4",
    "rollup-plugin-terser": "^7.0.2",
    "terser": "^5.7.0"
  },
  "module": "./esm/index.js",
  "type": "module",
  "exports": {
    ".": {
      "import": "./esm/index.js",
      "default": "./cjs/index.js"
    },
    "./async": {
      "import": "./esm/async.js",
      "default": "./cjs/async.js"
    },
    "./e": {
      "import": "./esm/e.js",
      "default": "./cjs/e.js"
    },
    "./extras": {
      "import": "./esm/extras.js",
      "default": "./cjs/extras.js"
    },
    "./package.json": "./package.json"
  },
  "unpkg": "min.js",
  "dependencies": {
    "@webreflection/lie": "^0.1.1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/WebReflection/uhooks.git"
  },
  "bugs": {
    "url": "https://github.com/WebReflection/uhooks/issues"
  },
  "homepage": "https://github.com/WebReflection/uhooks#readme"
}


<|start_filename|>cjs/extras.js<|end_filename|>
'use strict';
/*! (c) <NAME> - ISC */

const {useMemo} = require('./memo.js');
const {useLayoutEffect} = require('./effect.js');
const {useState} = require('./reducer.js');
const {useRef} = require('./ref.js');

/**
 * Persist the same reference while updating its current value.
 * @param {any} value
 * @returns {object} An always-same reference.
 */
const useUpdatedRef = value => {
  const ref = useRef(value);
  ref.current = value;
  return ref;
};
exports.useUpdatedRef = useUpdatedRef;

/**
 * Given a `MutationObserverInit` object with two extra properties,
 * such as `mutations(records){}`, and an optional `ref` to recycle,
 * observes the reference on layout effect and invokes `mutations`.
 * @param {MutationObserverInit} options
 * @returns {object} An always-same reference.
 */
const useMutationObserver = options => {
  const fn = useUpdatedRef(options.mutations);
  const ref = options.ref || useRef(null);
  const _ = [];
  const mo = useMemo(
    () => new MutationObserver(
      records => { fn.current(records); }
    ),
    _
  );
  useLayoutEffect(
    () => {
      mo.observe(ref.current, options);
      return () => { mo.disconnect(); };
    },
    _
  );
  return ref;
};
exports.useMutationObserver = useMutationObserver;

const defaultFetcher = url => fetch(url).then(res => res.json());

/**
 * A simplified version of the vercel useSWR
 * @see https://github.com/vercel/swr
 * @param {string|function} key the `url` to fetch
 * @param {function?} fetcher a function to fetch the `url`
 * @param {object?} options a configuration object
 */
const useSWR = (key, fetcher, options) => {
  const path = typeof key === 'function' ? key() : key;
  if (typeof fetcher === 'object') {
    options = fetcher;
    fetcher = null;
  }
  if (!options)
    options = {};
  if (!fetcher)
    fetcher = options.fetcher || defaultFetcher;
  const [data, update] = useState(options.initialData);
  const ref = useRef(null);
  if (!ref.current || path !== ref.current)
    fetcher(ref.current = path).then(update, update);
  const isError = data instanceof Error;
  return {
    data: isError ? null : data,
    error: isError ? data : null
  };
};
exports.useSWR = useSWR;

/**
 * A simplified version of React `useDebugValue`
 * @param {any} data data to show
 * @param {function?} callback callback to transform data
 */
const useDebugValue = (data, callback = data => data) => {
  console.log('%cµhooks:', 'font-weight:bold', callback(data));
};
exports.useDebugValue = useDebugValue;


<|start_filename|>test/e.js<|end_filename|>
const {hooked, useState, useRef} = require('../cjs/e');

const context = new Set;
const args = new Set;
const driver = {};

const test = hooked(function () {
  context.add(this);
  args.add(arguments);
  const [state1, update1] = useState(1);
  const [state2, update2] = useState(2);
  const ref1 = useRef(1);
  const ref2 = useRef(2);
  driver.states = [
    [state1, update1],
    [state2, update2]
  ];
  driver.refs = [
    ref1,
    ref2
  ];
});

test.call(Object, 1, 2, 3);

console.assert([...context][0] === Object, 'unexpected context');
console.assert([].slice.call([...args][0]).join(',') === '1,2,3', 'unexpected arguments');

console.assert(driver.states[0][0] === 1);
console.assert(driver.states[1][0] === 2);
console.assert(driver.refs[0].current === 1);
console.assert(driver.refs[1].current === 2);

driver.refs[1].current = 3;
driver.states[1][1](3);

console.assert(driver.states[0][0] === 1);
console.assert(driver.states[1][0] === 3);
console.assert(driver.refs[0].current === 1);
console.assert(driver.refs[1].current === 3);

console.assert(context.size === 1);
console.assert(args.size === 2);

driver.refs[0].current = 2;
driver.states[0][1](2);

console.assert(driver.states[0][0] === 2);
console.assert(driver.states[1][0] === 3);
console.assert(driver.refs[0].current === 2);
console.assert(driver.refs[1].current === 3);

console.assert(context.size === 1);
console.assert(args.size === 3);


<|start_filename|>esm/async.js<|end_filename|>
import {
  hooked as $hooked,
  dropEffect as $dropEffect,
  hasEffect as $hasEffect
} from './hooks.js';

const hooks = new WeakMap;

export const dropEffect = hook => $dropEffect(hooks.get(hook));

export const hasEffect = hook => $hasEffect(hooks.get(hook));

export const hooked = callback => {
  const $hook = $hooked(callback);
  hooks.set(hook, $hook);
  return hook;
  async function hook() {
    return await $hook.apply(this, arguments);
  }
};

export {
  wait,
  createContext, useContext,
  useCallback, useMemo,
  useEffect, useLayoutEffect,
  useReducer, useState,
  useRef
} from './index.js';


<|start_filename|>esm/index.js<|end_filename|>
/*! (c) <NAME> - ISC */

export {hooked, wait, dropEffect, hasEffect} from './hooks.js';
export {createContext, useContext} from './context.js';
export {useCallback, useMemo} from './memo.js';
export {useEffect, useLayoutEffect} from './effect.js';
export {useReducer, useState} from './reducer.js';
export {useRef} from './ref.js';


<|start_filename|>test/timer.js<|end_filename|>
const {hooked, useState, useEffect} = require('../cjs');

const Counter = hooked(name => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const time = setTimeout(setCount, 1000, count + 1);
    return () => clearTimeout(time);
  }, [count]);

  console.log(`${name}: ${count}`);
});

Counter('Hit');


<|start_filename|>test/index.js<|end_filename|>
const {
  hooked,
  hasEffect, dropEffect,
  createContext, useContext,
  useCallback, useMemo,
  useEffect, useLayoutEffect,
  useReducer, useState,
  useRef
} = require('../cjs');

const Counter = (start) => {
  const [count, setCount] = useState(start);
  const {current} = useRef({});
  current.increment = () => {
    setCount(count + 1);
  };
  console.log(count);
  return current;
};

const comp = hooked(Counter)(1);
comp.increment();

(async () => {

  let test = function () {
    console.assert(arguments.length === 1 && arguments[0] === 123);
    console.assert(this == 1);
  };
  hooked(test).call(1, 123);

  test = function () {
    const [value, update] = useState(0);
    if (value === 0)
      update(value + 1);
    else
      console.assert(value === 1);
  };
  hooked(test)();

  test = function () {
    const [value, update] = useState(() => 0);
    if (value === 0)
      update(() => value + 2);
    else
      console.assert(value === 2);
  };
  hooked(test)();

  test = function () {
    const [value, update] = useReducer((_, curr) => curr, -1, () => 0);
    const [other, refresh] = useReducer((_, curr) => curr, -1, () => 0);
    if (value === 0) {
      update(value + 1);
      refresh(other + 2);
    }
    else {
      console.assert(value === 1);
      console.assert(other === 2);
    }
  };
  hooked(test)();

  test = function () {
    const ref = useRef(null);
    const [value, update] = useState(ref.current);
    if (value === null)
      update(ref.current = 123);
    else
      console.assert(ref.current === 123);
  };
  hooked(test)();

  test = hooked(function () {
    const [value, update] = useState(() => 0);
    let i = 0, y = 1;
    useLayoutEffect(() => {
      console.assert(i === 0);
    });
    useEffect(() => {
      return () => {
        y = 2;
      };
    });
    if (value === 0)
      update(1);
  });
  test();
  test();

  let i = 0;
  test = hooked(function () {
    const [value, update] = useState(() => 0);
    useEffect(() => {
      console.assert(i++ === 0);
      return () => { console.log('dropEffect'); };
    }, []);
    if (value === 0)
      update(1);
  });
  test();
  console.assert(hasEffect(test));
  dropEffect(test);

  test = hooked(function () {
    const [count, setCount] = useState(0);
    const handleCount = useCallback(() => setCount(count + 1), [count]);
    if (count === 0)
      handleCount();
  });
  test();
  dropEffect(test);

  test = function () {
    const [count, setCount] = useState(0);
    const handleCount = useCallback(() => setCount(count + 1));
    if (count === 0)
      handleCount();
  };
  hooked(test)();

  let ctx = createContext(1);
  test = function () {
    const value = useContext(ctx);
    useEffect(() => {
      if (value === 1) {
        ctx.provide(value + 1);
        ctx.provide(value + 1);
      }
    }, [value]);
  };
  hooked(test)();

  test = function () {
    const [count, setCount] = useState(0);
    const current = useMemo(() => count, [count]);
    const future = useMemo(() => count, []);
    if (current === future)
      setCount(current + 1);
  };
  hooked(test)();
})();


<|start_filename|>e.js<|end_filename|>
/*! (c) <NAME> - ISC */
const uhooks = ((h, o, O, K) => ({
  hooked: f => {
    const k = {i: 0, s: []};
    return function H() {
      const p = h, r = o, e = O, v = K;
      h = H; o = this; O = arguments; K = k;
      K.i = 0;
      try { return f.apply(o, O); }
      finally {
        h = p; o = r; O = e; K = v;
      }
    };
  },
  useRef: current => {
    if (K.i == K.s.length)
      K.s.push({current: current});
    return K.s[K.i++];
  },
  useState: value => {
    const y = h, u = o, p = O, i = K.i, s = K.s;
    if (i == s.length)
      s.push(value);
    return [
      s[K.i++],
      value => {
        s[i] = value;
        y.apply(u, p);
      }
    ];
  }
}))();

const hooked = uhooks.hooked;
const useRef = uhooks.useRef;
const useState = uhooks.useState;


<|start_filename|>esm/reducer.js<|end_filename|>
import {getInfo, isFunction, reschedule} from './hooks.js';

const getValue = (value, f) => isFunction(f) ? f(value) : f;

export const useReducer = (reducer, value, init) => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length)
    s.push({
      $: isFunction(init) ?
          init(value) : getValue(void 0, value),
      set: value => {
        s[i].$ = reducer(s[i].$, value);
        reschedule(info);
      }
    });
  const {$, set} = s[info.i++];
  return [$, set];
};

export const useState = value => useReducer(getValue, value);


<|start_filename|>esm/ref.js<|end_filename|>
import {getInfo} from './hooks.js';

export const useRef = current => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length)
    s.push({current});
  return s[info.i++];
};


<|start_filename|>esm/memo.js<|end_filename|>
import {different, getInfo} from './hooks.js';

export const useCallback = (fn, guards) => useMemo(() => fn, guards);

export const useMemo = (memo, guards) => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length || !guards || guards.some(different, s[i]._))
    s[i] = {$: memo(), _: guards};
  return s[info.i++].$;
};


<|start_filename|>cjs/memo.js<|end_filename|>
'use strict';
const {different, getInfo} = require('./hooks.js');

const useCallback = (fn, guards) => useMemo(() => fn, guards);
exports.useCallback = useCallback;

const useMemo = (memo, guards) => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length || !guards || guards.some(different, s[i]._))
    s[i] = {$: memo(), _: guards};
  return s[info.i++].$;
};
exports.useMemo = useMemo;


<|start_filename|>cjs/context.js<|end_filename|>
'use strict';
const {getInfo} = require('./hooks.js');

const createContext = value => ({
  _: new Set,
  provide,
  value
});
exports.createContext = createContext;

const useContext = ({_, value}) => {
  _.add(getInfo());
  return value;
};
exports.useContext = useContext;

function provide(newValue) {
  const {_, value} = this;
  if (value !== newValue) {
    this._ = new Set;
    this.value = newValue;
    _.forEach(({h, c, a}) => {
      h.apply(c, a);
    });
  }
}


<|start_filename|>cjs/hooks.js<|end_filename|>
'use strict';
const Lie = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('@webreflection/lie'));

let info = null, schedule = new Set;

const invoke = effect => {
  const {$, r, h} = effect;
  if (isFunction(r)) {
    fx.get(h).delete(effect);
    r();
  }
  if (isFunction(effect.r = $()))
    fx.get(h).add(effect);
};

const runSchedule = () => {
  const previous = schedule;
  schedule = new Set;
  previous.forEach(({h, c, a, e}) => {
    // avoid running schedules when the hook is
    // re-executed before such schedule happens
    if (e)
      h.apply(c, a);
  });
};

const fx = new WeakMap;
exports.fx = fx;
const effects = [];
exports.effects = effects;
const layoutEffects = [];
exports.layoutEffects = layoutEffects;

function different(value, i) {
  return value !== this[i];
}
exports.different = different;

const dropEffect = hook => {
  const effects = fx.get(hook);
  if (effects)
    wait.then(() => {
      effects.forEach(effect => {
        effect.r();
        effect.r = null;
      });
      effects.clear();
    });
};
exports.dropEffect = dropEffect;

const getInfo = () => info;
exports.getInfo = getInfo;

const hasEffect = hook => fx.has(hook);
exports.hasEffect = hasEffect;

const isFunction = f => typeof f === 'function';
exports.isFunction = isFunction;

const hooked = callback => {
  const current = {h: hook, c: null, a: null, e: 0, i: 0, s: []};
  return hook;
  function hook() {
    const prev = info;
    info = current;
    current.e = current.i = 0;
    try {
      return callback.apply(current.c = this, current.a = arguments);
    }
    finally {
      info = prev;
      if (effects.length)
        wait.then(effects.forEach.bind(effects.splice(0), invoke));
      if (layoutEffects.length)
        layoutEffects.splice(0).forEach(invoke);
    }
  }
};
exports.hooked = hooked;

const reschedule = info => {
  if (!schedule.has(info)) {
    info.e = 1;
    schedule.add(info);
    wait.then(runSchedule);
  }
};
exports.reschedule = reschedule;

const wait = new Lie($ => $());
exports.wait = wait;


<|start_filename|>index.js<|end_filename|>
self.uhooks = (function (exports) {
  'use strict';

  var Lie = typeof Promise === 'function' ? Promise : function (fn) {
    var queue = [],
        resolved = 0,
        value;
    fn(function ($) {
      value = $;
      resolved = 1;
      queue.splice(0).forEach(then);
    });
    return {
      then: then
    };

    function then(fn) {
      return resolved ? setTimeout(fn, 0, value) : queue.push(fn), this;
    }
  };

  var info = null,
      schedule = new Set();

  var invoke = function invoke(effect) {
    var $ = effect.$,
        r = effect.r,
        h = effect.h;

    if (isFunction(r)) {
      fx.get(h)["delete"](effect);
      r();
    }

    if (isFunction(effect.r = $())) fx.get(h).add(effect);
  };

  var runSchedule = function runSchedule() {
    var previous = schedule;
    schedule = new Set();
    previous.forEach(function (_ref) {
      var h = _ref.h,
          c = _ref.c,
          a = _ref.a,
          e = _ref.e;
      // avoid running schedules when the hook is
      // re-executed before such schedule happens
      if (e) h.apply(c, a);
    });
  };

  var fx = new WeakMap();
  var effects = [];
  var layoutEffects = [];
  function different(value, i) {
    return value !== this[i];
  }
  var dropEffect = function dropEffect(hook) {
    var effects = fx.get(hook);
    if (effects) wait.then(function () {
      effects.forEach(function (effect) {
        effect.r();
        effect.r = null;
      });
      effects.clear();
    });
  };
  var getInfo = function getInfo() {
    return info;
  };
  var hasEffect = function hasEffect(hook) {
    return fx.has(hook);
  };
  var isFunction = function isFunction(f) {
    return typeof f === 'function';
  };
  var hooked = function hooked(callback) {
    var current = {
      h: hook,
      c: null,
      a: null,
      e: 0,
      i: 0,
      s: []
    };
    return hook;

    function hook() {
      var prev = info;
      info = current;
      current.e = current.i = 0;

      try {
        return callback.apply(current.c = this, current.a = arguments);
      } finally {
        info = prev;
        if (effects.length) wait.then(effects.forEach.bind(effects.splice(0), invoke));
        if (layoutEffects.length) layoutEffects.splice(0).forEach(invoke);
      }
    }
  };
  var reschedule = function reschedule(info) {
    if (!schedule.has(info)) {
      info.e = 1;
      schedule.add(info);
      wait.then(runSchedule);
    }
  };
  var wait = new Lie(function ($) {
    return $();
  });

  var createContext = function createContext(value) {
    return {
      _: new Set(),
      provide: provide,
      value: value
    };
  };
  var useContext = function useContext(_ref) {
    var _ = _ref._,
        value = _ref.value;

    _.add(getInfo());

    return value;
  };

  function provide(newValue) {
    var _ = this._,
        value = this.value;

    if (value !== newValue) {
      this._ = new Set();
      this.value = newValue;

      _.forEach(function (_ref2) {
        var h = _ref2.h,
            c = _ref2.c,
            a = _ref2.a;
        h.apply(c, a);
      });
    }
  }

  var useCallback = function useCallback(fn, guards) {
    return useMemo(function () {
      return fn;
    }, guards);
  };
  var useMemo = function useMemo(memo, guards) {
    var info = getInfo();
    var i = info.i,
        s = info.s;
    if (i === s.length || !guards || guards.some(different, s[i]._)) s[i] = {
      $: memo(),
      _: guards
    };
    return s[info.i++].$;
  };

  var createEffect = function createEffect(stack) {
    return function (callback, guards) {
      var info = getInfo();
      var i = info.i,
          s = info.s,
          h = info.h;
      var call = i === s.length;
      info.i++;

      if (call) {
        if (!fx.has(h)) fx.set(h, new Set());
        s[i] = {
          $: callback,
          _: guards,
          r: null,
          h: h
        };
      }

      if (call || !guards || guards.some(different, s[i]._)) stack.push(s[i]);
      s[i].$ = callback;
      s[i]._ = guards;
    };
  };

  var useEffect = createEffect(effects);
  var useLayoutEffect = createEffect(layoutEffects);

  var getValue = function getValue(value, f) {
    return isFunction(f) ? f(value) : f;
  };

  var useReducer = function useReducer(reducer, value, init) {
    var info = getInfo();
    var i = info.i,
        s = info.s;
    if (i === s.length) s.push({
      $: isFunction(init) ? init(value) : getValue(void 0, value),
      set: function set(value) {
        s[i].$ = reducer(s[i].$, value);
        reschedule(info);
      }
    });
    var _s$info$i = s[info.i++],
        $ = _s$info$i.$,
        set = _s$info$i.set;
    return [$, set];
  };
  var useState = function useState(value) {
    return useReducer(getValue, value);
  };

  var useRef = function useRef(current) {
    var info = getInfo();
    var i = info.i,
        s = info.s;
    if (i === s.length) s.push({
      current: current
    });
    return s[info.i++];
  };

  exports.createContext = createContext;
  exports.dropEffect = dropEffect;
  exports.hasEffect = hasEffect;
  exports.hooked = hooked;
  exports.useCallback = useCallback;
  exports.useContext = useContext;
  exports.useEffect = useEffect;
  exports.useLayoutEffect = useLayoutEffect;
  exports.useMemo = useMemo;
  exports.useReducer = useReducer;
  exports.useRef = useRef;
  exports.useState = useState;
  exports.wait = wait;

  return exports;

}({}));


<|start_filename|>esm/context.js<|end_filename|>
import {getInfo} from './hooks.js';

export const createContext = value => ({
  _: new Set,
  provide,
  value
});

export const useContext = ({_, value}) => {
  _.add(getInfo());
  return value;
};

function provide(newValue) {
  const {_, value} = this;
  if (value !== newValue) {
    this._ = new Set;
    this.value = newValue;
    _.forEach(({h, c, a}) => {
      h.apply(c, a);
    });
  }
}


<|start_filename|>cjs/ref.js<|end_filename|>
'use strict';
const {getInfo} = require('./hooks.js');

const useRef = current => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length)
    s.push({current});
  return s[info.i++];
};
exports.useRef = useRef;


<|start_filename|>cjs/async.js<|end_filename|>
'use strict';
const {
  hooked: $hooked,
  dropEffect: $dropEffect,
  hasEffect: $hasEffect
} = require('./hooks.js');

const hooks = new WeakMap;

const dropEffect = hook => $dropEffect(hooks.get(hook));
exports.dropEffect = dropEffect;

const hasEffect = hook => $hasEffect(hooks.get(hook));
exports.hasEffect = hasEffect;

const hooked = callback => {
  const $hook = $hooked(callback);
  hooks.set(hook, $hook);
  return hook;
  async function hook() {
    return await $hook.apply(this, arguments);
  }
};
exports.hooked = hooked;

(m => {
  exports.wait = m.wait;
  exports.createContext = m.createContext;
  exports.useContext = m.useContext;
  exports.useCallback = m.useCallback;
  exports.useMemo = m.useMemo;
  exports.useEffect = m.useEffect;
  exports.useLayoutEffect = m.useLayoutEffect;
  exports.useReducer = m.useReducer;
  exports.useState = m.useState;
  exports.useRef = m.useRef;
})(require('./index.js'));


<|start_filename|>esm/hooks.js<|end_filename|>
import Lie from '@webreflection/lie';

let info = null, schedule = new Set;

const invoke = effect => {
  const {$, r, h} = effect;
  if (isFunction(r)) {
    fx.get(h).delete(effect);
    r();
  }
  if (isFunction(effect.r = $()))
    fx.get(h).add(effect);
};

const runSchedule = () => {
  const previous = schedule;
  schedule = new Set;
  previous.forEach(({h, c, a, e}) => {
    // avoid running schedules when the hook is
    // re-executed before such schedule happens
    if (e)
      h.apply(c, a);
  });
};

export const fx = new WeakMap;
export const effects = [];
export const layoutEffects = [];

export function different(value, i) {
  return value !== this[i];
};

export const dropEffect = hook => {
  const effects = fx.get(hook);
  if (effects)
    wait.then(() => {
      effects.forEach(effect => {
        effect.r();
        effect.r = null;
      });
      effects.clear();
    });
};

export const getInfo = () => info;

export const hasEffect = hook => fx.has(hook);

export const isFunction = f => typeof f === 'function';

export const hooked = callback => {
  const current = {h: hook, c: null, a: null, e: 0, i: 0, s: []};
  return hook;
  function hook() {
    const prev = info;
    info = current;
    current.e = current.i = 0;
    try {
      return callback.apply(current.c = this, current.a = arguments);
    }
    finally {
      info = prev;
      if (effects.length)
        wait.then(effects.forEach.bind(effects.splice(0), invoke));
      if (layoutEffects.length)
        layoutEffects.splice(0).forEach(invoke);
    }
  }
};

export const reschedule = info => {
  if (!schedule.has(info)) {
    info.e = 1;
    schedule.add(info);
    wait.then(runSchedule);
  }
};

export const wait = new Lie($ => $());


<|start_filename|>cjs/reducer.js<|end_filename|>
'use strict';
const {getInfo, isFunction, reschedule} = require('./hooks.js');

const getValue = (value, f) => isFunction(f) ? f(value) : f;

const useReducer = (reducer, value, init) => {
  const info = getInfo();
  const {i, s} = info;
  if (i === s.length)
    s.push({
      $: isFunction(init) ?
          init(value) : getValue(void 0, value),
      set: value => {
        s[i].$ = reducer(s[i].$, value);
        reschedule(info);
      }
    });
  const {$, set} = s[info.i++];
  return [$, set];
};
exports.useReducer = useReducer;

const useState = value => useReducer(getValue, value);
exports.useState = useState;


<|start_filename|>cjs/effect.js<|end_filename|>
'use strict';
const {fx, effects, layoutEffects, different, getInfo} = require('./hooks.js');

const createEffect = stack => (callback, guards) => {
  const info = getInfo();
  const {i, s, h} = info;
  const call = i === s.length;
  info.i++;
  if (call) {
    if (!fx.has(h))
      fx.set(h, new Set);
    s[i] = {$: callback, _: guards, r: null, h};
  }
  if (call || !guards || guards.some(different, s[i]._))
    stack.push(s[i]);
  s[i].$ = callback;
  s[i]._ = guards;
};

const useEffect = createEffect(effects);
exports.useEffect = useEffect;

const useLayoutEffect = createEffect(layoutEffects);
exports.useLayoutEffect = useLayoutEffect;


<|start_filename|>test/context.js<|end_filename|>
const {
  hooked,
  createContext, useContext,
} = require('../cjs');

const ctx = createContext(0);

const log = () => {
  console.log(useContext(ctx));
};

const log1 = hooked(log);

const log2 = hooked(log);

log1();
log2();

setTimeout(() => {
  ctx.provide(1);
});


<|start_filename|>cjs/index.js<|end_filename|>
'use strict';
/*! (c) <NAME> - ISC */

(m => {
  exports.hooked = m.hooked;
  exports.wait = m.wait;
  exports.dropEffect = m.dropEffect;
  exports.hasEffect = m.hasEffect;
})(require('./hooks.js'));
(m => {
  exports.createContext = m.createContext;
  exports.useContext = m.useContext;
})(require('./context.js'));
(m => {
  exports.useCallback = m.useCallback;
  exports.useMemo = m.useMemo;
})(require('./memo.js'));
(m => {
  exports.useEffect = m.useEffect;
  exports.useLayoutEffect = m.useLayoutEffect;
})(require('./effect.js'));
(m => {
  exports.useReducer = m.useReducer;
  exports.useState = m.useState;
})(require('./reducer.js'));
(m => {
  exports.useRef = m.useRef;
})(require('./ref.js'));
