vike
Version:
The Framework *You* Control - Next.js & Nuxt alternative for unprecedented flexibility and dependability.
146 lines (145 loc) • 6.31 kB
JavaScript
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;
});
}