@sigi/ssr
Version:
Server side rendering support for sigi framework
130 lines • 4.97 kB
JavaScript
import { TERMINATE_ACTION_TYPE_SYMBOL, getSSREffectMeta, RETRY_ACTION_TYPE_SYMBOL } from '@sigi/core';
import { rootInjector } from '@sigi/di';
import { StateToPersist } from './state-to-persist';
export const SKIP_SYMBOL = Symbol('skip-symbol');
export const runSSREffects = (ctx, modules, config = {}) => {
const stateToSerialize = {};
const actionsToRetry = {};
const { providers, timeout = 1 } = config;
const injector = rootInjector.createChild([...modules, ...(providers ?? [])]);
const cleanupFns = [];
let timer;
let terminatedCount = 0;
let effectsCount = 0;
const moduleInstanceCache = new Map();
injector.serverCache = moduleInstanceCache;
const pendingState = new Promise((resolve, reject) => {
if (!modules.length) {
return resolve();
}
timer = setTimeout(() => {
reject(new Error('Terminate timeout'));
}, timeout * 1000);
for (const constructor of modules) {
let isAllSkipped = true;
const ssrActionsMeta = getSSREffectMeta(constructor.prototype, []);
const effectModuleInstance = injector.getInstance(constructor);
moduleInstanceCache.set(constructor, effectModuleInstance);
const { store, moduleName } = effectModuleInstance;
effectsCount += ssrActionsMeta.length;
const subscription = store.action$.subscribe({
next: ({ type, payload }) => {
isAllSkipped = false;
if (type === RETRY_ACTION_TYPE_SYMBOL) {
const { name } = payload;
if (!actionsToRetry[moduleName]) {
actionsToRetry[moduleName] = [name];
}
else {
actionsToRetry[moduleName].push(name);
}
}
if (type === TERMINATE_ACTION_TYPE_SYMBOL) {
terminatedCount++;
}
if (terminatedCount === effectsCount) {
resolve();
}
},
error: (e) => {
reject(e);
},
});
for (const ssrActionMeta of ssrActionsMeta) {
if (ssrActionMeta.payloadGetter) {
let maybeDeferredPayload;
try {
maybeDeferredPayload = ssrActionMeta.payloadGetter(ctx, SKIP_SYMBOL);
}
catch (e) {
return reject(e);
}
Promise.resolve(maybeDeferredPayload)
.then((payload) => {
if (payload !== SKIP_SYMBOL) {
isAllSkipped = false;
store.dispatch({
type: ssrActionMeta.action,
payload,
store,
});
}
else {
if (!actionsToRetry[moduleName]) {
actionsToRetry[moduleName] = [ssrActionMeta.action];
}
else {
actionsToRetry[moduleName].push(ssrActionMeta.action);
}
effectsCount--;
if (terminatedCount === effectsCount) {
resolve();
}
}
})
.catch((e) => {
reject(e);
});
}
else {
isAllSkipped = false;
store.dispatch({
type: ssrActionMeta.action,
payload: undefined,
store,
});
}
}
cleanupFns.push(() => {
subscription.unsubscribe();
store.dispose();
!isAllSkipped && (stateToSerialize[moduleName] = store.state);
});
}
if (!effectsCount) {
resolve();
}
})
.then(() => {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
for (const cleanup of cleanupFns) {
cleanup();
}
return new StateToPersist(stateToSerialize, actionsToRetry);
})
.catch((e) => {
if (timer) {
clearTimeout(timer);
timer = undefined;
}
for (const cleanup of cleanupFns) {
cleanup();
}
throw e;
});
return { injector, pendingState };
};
//# sourceMappingURL=run.js.map