UNPKG

@ryo-98/react-api-bridge

Version:
428 lines (424 loc) 14.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // ../src/bridge.tsx var bridge_exports = {}; __export(bridge_exports, { default: () => bridge_default }); module.exports = __toCommonJS(bridge_exports); var import_react = __toESM(require("react")); // ../src/utils.ts var removeArrayElement = (entity, element) => { if (Array.isArray(entity)) { const deleteIndex = entity.findIndex((r) => r === element); if (deleteIndex > -1) { entity.splice(deleteIndex, 1); } } }; function tryInvoke(mayFn) { if (typeof mayFn === "function") { mayFn(); return true; } return false; } var prefixes = []; var lastOccupiedNumber = 0; var genHookId = () => { if (lastOccupiedNumber < Number.MAX_SAFE_INTEGER) { lastOccupiedNumber++; } else { prefixes.push(String.fromCharCode(prefixes.length)); lastOccupiedNumber = 0; } return [...prefixes, lastOccupiedNumber].join(""); }; function appendToMappedValue(map, key, element) { let arr = map.get(key); if (typeof arr === "undefined") { arr = [element]; map.set(key, arr); } else { arr.push(element); } return () => { removeArrayElement(arr, element); if (arr?.length === 0) { map.delete(key); } }; } // ../src/bridge.tsx var createBridge = (...args) => { const payload = args[0]; const output = genOutput(payload); const currying = (options) => { return genOutput(payload, options); }; return Object.assign(currying, output); }; var bridge_default = createBridge; function genOutput(payload, bridgeOptions) { const defaultContextValue = { bridge: {}, parent: void 0, payload }; const BridgeContext = (0, import_react.createContext)(defaultContextValue); const cacheInitCbMap = /* @__PURE__ */ new WeakMap(); const pendingResolverMap = /* @__PURE__ */ new Map(); const _getPendingContextMap = (_apiNList) => { return pendingResolverMap.get(_apiNList); }; const _setPendingContextMap = (_apiNList, map) => { return pendingResolverMap.set(_apiNList, map); }; function getIsMulti(name) { return bridgeOptions?.[name]?.isMulti; } const _getApiDesc = (name, source) => { let isInitial = false; if (!source[name]) { isInitial = true; const isMulti = getIsMulti(name); const apiNList = isMulti ? [] : (0, import_react.createRef)(); source[name] = { options: bridgeOptions?.[name], apiNList }; } return { apiNList: source[name].apiNList, isInitial }; }; const Boundary = (0, import_react.forwardRef)((props, ref) => { const _ownBridge = (0, import_react.useMemo)(() => ({}), []); const bridge = props.contextValue?.bridge || _ownBridge; const paramParent = props.contextValue?.parent; const upperParent = (0, import_react.useContext)(BridgeContext); const parent = paramParent || upperParent; const _payload = props.contextValue?.payload ?? props.payload; (0, import_react.useImperativeHandle)(ref, () => { return { getAPI: (name) => { return _getApiDesc(name, bridge).apiNList; }, parent, payload: _payload }; }, [_payload]); const contextValue = (0, import_react.useMemo)(() => { const final = { ...props.contextValue || { bridge, parent }, payload: _payload }; return final; }, [props.contextValue, _payload]); return /* @__PURE__ */ import_react.default.createElement(BridgeContext.Provider, { value: contextValue }, props.children); }); Boundary.displayName = "Boundary"; const useFinalContextValue = (options) => { const { contextValue: _outerContextValue } = options || {}; const ownContextValue = (0, import_react.useContext)(BridgeContext); return _outerContextValue || ownContextValue; }; const initializedOnInitMap = /* @__PURE__ */ new Map(); function mountHookInitEffect(name, onInit, apiNList, hookId) { if (!onInit) return; const isMulti = getIsMulti(name) ?? false; const involvedApiList = []; let deferFn; if (apiNList && !isMulti) { const _assertedApi = apiNList; if (!_assertedApi.current) return; const _assertedOnInit = onInit; deferFn = () => { return _assertedOnInit(_assertedApi); }; involvedApiList.push(_assertedApi); } else if (apiNList && isMulti) { const _assertedApiList = apiNList; if (!_assertedApiList.length) return; const _assertedOnInit = onInit; deferFn = () => { return _assertedOnInit(void 0, _assertedApiList); }; involvedApiList.push(..._assertedApiList); } else { throw new Error("This might the internal Error of react-api-bridge"); } let clearFns = []; if (involvedApiList.some((apiRef) => !initializedOnInitMap.get(apiRef)?.includes(hookId))) { clearFns.push( deferFn?.(), ...involvedApiList.map((apiRef) => { return appendToMappedValue(initializedOnInitMap, apiRef, hookId); }) ); } return () => { clearFns.forEach(tryInvoke); }; } function useInitEffect(onInit, name, apiNList, contextValue) { const hookId = useHookId(); (0, import_react.useEffect)(() => { if (!onInit) return; const removeCache = appendToMappedValue(cacheInitCbMap, apiNList, { onInit, hookId }); const clearInitEffect = mountHookInitEffect(name, onInit, apiNList, hookId); return () => { removeCache(); clearInitEffect?.(); }; }, []); (0, import_react.useEffect)(() => { if (!onInit) return; const cacheCbs = cacheInitCbMap.get(apiNList); cacheCbs?.forEach((couple) => { if (couple.hookId === hookId) { couple.onInit = onInit; } }); }, [onInit]); (0, import_react.useEffect)(() => { return () => { initializedOnInitMap.forEach((arr, key) => { removeArrayElement(arr, hookId); }); }; }, []); } function _getUpperContextValue(start, shouldForwardYield) { let parent = start.parent; while (true) { if (!parent) break; if (!shouldForwardYield) break; const apiNames = Object.keys(parent.bridge); const allAPI = apiNames.reduce((all, apiName) => { const bridge = parent.bridge[apiName]; all[apiName] = bridge.apiNList; return all; }, {}); if (!!shouldForwardYield({ payload: parent.payload, parent: parent.parent, allAPI })) break; parent = parent.parent; } return parent; } function _getUpperApiDesc(contextValue, _name, options) { const parent = _getUpperContextValue(options?.contextValue || contextValue, options?.shouldForwardYield); if (!parent) return; return _getApiDesc(_name, parent.bridge); } function _getResolverOrCreateWhenMissing(apiNList, contextValue, initial) { let contextValueMap = _getPendingContextMap(apiNList); if (!contextValueMap) { contextValueMap = /* @__PURE__ */ new Map(); _setPendingContextMap(apiNList, contextValueMap); } let resolvers = contextValueMap.get(contextValue); if (!resolvers) { resolvers = []; contextValueMap.set(contextValue, resolvers); } let resolver = resolvers.find((r) => r.initial); if (!resolver || !initial) { let outerResolve; const newPromise = new Promise((resolve) => { outerResolve = resolve; }); resolver = { initial, promise: newPromise, resolve: outerResolve }; resolvers.push(resolver); } return { resolver, contextValueMap, resolvers }; } return { Boundary, initCbMap: cacheInitCbMap, initializedCallbacksMap: initializedOnInitMap, getAPI(name, contextValue = defaultContextValue) { return _getApiDesc(name, contextValue.bridge).apiNList; }, getAPIAsync(name, options) { const { contextValue, initial } = { contextValue: defaultContextValue, initial: true, ...options }; const { apiNList } = _getApiDesc(name, contextValue.bridge); const { resolver } = _getResolverOrCreateWhenMissing(apiNList, contextValue, initial); return resolver.promise; }, useAPI: (name, hookOptions) => { const { onInit } = hookOptions || {}; const contextValue = useFinalContextValue(hookOptions); const { apiNList } = _getApiDesc(name, contextValue.bridge); useInitEffect(onInit, name, apiNList, contextValue); return apiNList; }, useBoundaryPayload: (hookOptions) => { const contextValue = useFinalContextValue(hookOptions); return contextValue.payload; }, useBoundaryRef: () => { return (0, import_react.useRef)(null); }, useContextValue: (...args) => { const payload2 = args[0]; const parent = (0, import_react.useContext)(BridgeContext); return (0, import_react.useMemo)(() => { return { bridge: {}, parent, payload: payload2 }; }, []); }, useRegister: (name, init, deps, hookOptions) => { const isMulti = getIsMulti(name); const contextValue = useFinalContextValue(hookOptions); const { apiNList } = (0, import_react.useMemo)(() => _getApiDesc(name, contextValue.bridge), [name, contextValue]); const apiRef = useUniqueElementRef(apiNList); const registerId = useHookId(); (0, import_react.useImperativeHandle)(apiRef, () => { return init(); }, deps); (0, import_react.useEffect)(() => { let clearFns = []; const { resolvers, resolver: initialResolver } = _getResolverOrCreateWhenMissing(apiNList, contextValue, true); resolvers.forEach((resolver) => { resolver.resolve(apiNList); }); resolvers.length = 0; resolvers.push(initialResolver); const callbacks = cacheInitCbMap.get(apiNList); callbacks?.filter((initInfo) => { return !initializedOnInitMap.get(apiRef)?.includes(initInfo.hookId); }).forEach((initInfo) => { clearFns.push(appendToMappedValue(initializedOnInitMap, apiRef, initInfo.hookId)); const onInit = initInfo.onInit; if (isMulti) { const _assertedOnInit = onInit; clearFns.push(_assertedOnInit(apiRef, apiNList)); } else { const _assertedOnInit = onInit; clearFns.push(_assertedOnInit(apiRef)); } }); return () => { clearFns.forEach(tryInvoke); initializedOnInitMap.delete(apiRef); }; }, []); }, useTools: (hookOptions) => { const contextValue = useFinalContextValue(hookOptions); const getAPI = (0, import_react.useCallback)((_name, options) => { return _getApiDesc(_name, options?.contextValue?.bridge || contextValue.bridge).apiNList; }, [contextValue.bridge]); const getBoundaryPayload = (0, import_react.useCallback)((options) => { return (options?.contextValue || contextValue).payload; }, [contextValue.payload]); const getUpperAPI = (0, import_react.useCallback)((_name, options) => { return _getUpperApiDesc(options?.contextValue || contextValue, _name, options)?.apiNList; }, []); const getUpperBoundaryPayload = (0, import_react.useCallback)((options) => { const parent = _getUpperContextValue(options?.contextValue || contextValue, options?.shouldForwardYield); if (!parent) return; return parent.payload; }, []); const getAPIAsync = (_name, options) => { const { initial } = { initial: true, ...options }; const { apiNList } = _getApiDesc(_name, (options?.contextValue || contextValue).bridge); const { resolver } = _getResolverOrCreateWhenMissing(apiNList, contextValue, initial); return resolver.promise; }; return { getAPI, getBoundaryPayload, getUpperAPI, getUpperBoundaryPayload, getAPIAsync }; }, useUpperAPI: (name, hookOptions) => { const { onInit } = hookOptions || {}; const contextValue = useFinalContextValue(hookOptions); const _apiNList = (0, import_react.useMemo)(() => { return _getUpperApiDesc(contextValue, name, hookOptions)?.apiNList; }, []); if (!_apiNList) return; useInitEffect(onInit, name, _apiNList, contextValue); return _apiNList; }, useUpperBoundaryPayload: (hookOptions) => { const { shouldForwardYield } = hookOptions || {}; const contextValue = useFinalContextValue(hookOptions); const boundaryContextValue = (0, import_react.useMemo)(() => { return _getUpperContextValue(contextValue, shouldForwardYield); }, []); return boundaryContextValue?.payload; } }; } function useUniqueElementRef(entity) { const elRef = (0, import_react.useRef)(null); (0, import_react.useEffect)(() => { if (Array.isArray(entity)) { entity.push(elRef); } return () => { removeArrayElement(entity, elRef); }; }, [entity]); return Array.isArray(entity) ? elRef : entity; } function useHookId() { return (0, import_react.useMemo)(() => { return true ? genHookId() : Symbol("hookId"); }, []); }