UNPKG

vike

Version:

The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.

146 lines (145 loc) 6.31 kB
export { execHook }; export { execHookGlobal }; export { execHookDirect }; export { execHookDirectSingle }; export { execHookDirectSingleWithReturn }; export { execHookDirectWithoutPageContext }; export { execHookDirectSync }; export { getPageContext }; export { providePageContext }; export { isUserHookError }; import { getProjectError, assertWarning, assertUsage } from '../../utils/assert.js'; import { getGlobalObject } from '../../utils/getGlobalObject.js'; import { humanizeTime } from '../../utils/humanizeTime.js'; import { isObject } from '../../utils/isObject.js'; import { getHookFromPageConfigGlobalCumulative, getHookFromPageContextNew } from './getHook.js'; import { preparePageContextForPublicUsage, } from '../preparePageContextForPublicUsage.js'; const globalObject = getGlobalObject('utils/execHook.ts', { userHookErrors: new WeakMap(), pageContext: null, }); async function execHook(hookName, pageContext, preparePageContextForPublicUsage) { const hooks = getHookFromPageContextNew(hookName, pageContext); return await execHookDirect(hooks, pageContext, preparePageContextForPublicUsage); } async function execHookGlobal(hookName, pageConfigGlobal, pageContext, hookArg, prepareForPublicUsage) { const hooks = getHookFromPageConfigGlobalCumulative(pageConfigGlobal, hookName); const hookArgForPublicUsage = prepareForPublicUsage(hookArg); await Promise.all(hooks.map(async (hook) => { await execHookDirectAsync(() => hook.hookFn(hookArgForPublicUsage), hook, pageContext); })); } async function execHookDirect(hooks, pageContext, preparePageContextForPublicUsage) { if (!hooks.length) return []; const pageContextForPublicUsage = preparePageContextForPublicUsage(pageContext); const hooksWithResult = await Promise.all(hooks.map(async (hook) => { const hookReturn = await execHookDirectAsync(() => hook.hookFn(pageContextForPublicUsage), hook, pageContextForPublicUsage); return { ...hook, hookReturn }; })); return hooksWithResult; } async function execHookDirectSingle(hook, pageContext, preparePageContextForPublicUsage) { const hooksWithResult = await execHookDirect([hook], pageContext, preparePageContextForPublicUsage); const { hookReturn } = hooksWithResult[0]; assertUsage(hookReturn === undefined, `The ${hook.hookName}() hook defined by ${hook.hookFilePath} isn't allowed to return a value`); } async function execHookDirectSingleWithReturn(hook, pageContext, preparePageContextForPublicUsage) { const hooksWithResult = await execHookDirect([hook], pageContext, preparePageContextForPublicUsage); const { hookReturn } = hooksWithResult[0]; return { hookReturn }; } function isUserHookError(err) { if (!isObject(err)) return false; return globalObject.userHookErrors.get(err) ?? false; } async function execHookDirectWithoutPageContext(hookFnCaller, hook) { const { hookName, hookFilePath, hookTimeout } = hook; const hookReturn = await execHookDirectAsync(hookFnCaller, { hookName, hookFilePath, hookTimeout }, null); return hookReturn; } function execHookDirectAsync(hookFnCaller, hook, pageContextForPublicUsage) { const { hookName, hookFilePath, hookTimeout: { error: timeoutErr, warning: timeoutWarn }, } = hook; let resolve; let reject; const promise = new Promise((resolve_, reject_) => { resolve = (ret) => { clearTimeouts(); resolve_(ret); }; reject = (err) => { clearTimeouts(); reject_(err); }; }); const clearTimeouts = () => { if (currentTimeoutWarn) clearTimeout(currentTimeoutWarn); if (currentTimeoutErr) clearTimeout(currentTimeoutErr); }; const currentTimeoutWarn = isNotDisabled(timeoutWarn) && setTimeout(() => { assertWarning(false, `The ${hookName}() hook defined by ${hookFilePath} is slow: it's taking more than ${humanizeTime(timeoutWarn)} (https://vike.dev/hooksTimeout)`, { onlyOnce: false }); }, timeoutWarn); const currentTimeoutErr = isNotDisabled(timeoutErr) && setTimeout(() => { const err = getProjectError(`The ${hookName}() hook defined by ${hookFilePath} timed out: it didn't finish after ${humanizeTime(timeoutErr)} (https://vike.dev/hooksTimeout)`); reject(err); }, timeoutErr); (async () => { try { providePageContextInternal(pageContextForPublicUsage); const ret = await hookFnCaller(); resolve(ret); } catch (err) { if (isObject(err)) { globalObject.userHookErrors.set(err, { hookName, hookFilePath }); } reject(err); } })(); return promise; } function execHookDirectSync(hook, pageContext, preparePageContextForPublicUsage) { const pageContextForPublicUsage = preparePageContextForPublicUsage(pageContext); providePageContextInternal(pageContextForPublicUsage); const hookReturn = hook.hookFn(pageContextForPublicUsage); return { hookReturn }; } function isNotDisabled(timeout) { return !!timeout && timeout !== Infinity; } /** * Access `pageContext` object inside Vike hooks, in order to create universal hooks. * * https://vike.dev/getPageContext */ function getPageContext() { const { pageContext } = globalObject; if (!pageContext) return null; const pageContextForPublicUsage = pageContext._isProxyObject ? // providePageContext() is called on the user-land (e.g. it's called by `vike-{react,vue,solid}`) thus it's already a proxy pageContext : preparePageContextForPublicUsage(pageContext); return pageContextForPublicUsage; } /** * Provide `pageContext` for universal hooks. * * https://vike.dev/getPageContext */ function providePageContext(pageContext) { providePageContextInternal(pageContext); } function providePageContextInternal(pageContext) { globalObject.pageContext = pageContext; // Promise.resolve() is quicker than process.nextTick() and setImmediate() // https://stackoverflow.com/questions/67949576/process-nexttick-before-promise-resolve-then Promise.resolve().then(() => { globalObject.pageContext = null; }); }