@nitrogenbuilder/client-core
Version:
Nitrogen Builder Core Client
149 lines (148 loc) • 5.85 kB
JavaScript
import { BatchFetchContext } from './BatchFetchContext.js';
import { logger } from './utils/logger.js';
import { updateModuleProps } from './utils/updateWithDefaultProps.js';
export class Nitrogen {
static instance;
static modules = new Map();
static moduleComponents = new Map();
static moduleDataFetchers = new Map();
static moduleEventQueued = false;
static nitrogenInitializedListenerAdded = false;
// The idea hereis that the moduleData map key is a cache key. The first item in the array is always the moduleId.
// This logic is not yet implemented. Right now it only works with one array item which is the moduleId.
static moduleData = new Map();
constructor() {
if (Nitrogen.instance) {
return Nitrogen.instance;
}
Nitrogen.instance = this;
}
getModule(moduleId) {
return Nitrogen.modules.get(`${moduleId}`);
}
getModuleComponent(moduleId) {
return Nitrogen.moduleComponents.get(`${moduleId}`);
}
getModuleData(moduleId) {
logger.log('getting Nitrogen.moduleData, ' + moduleId, { context: { moduleData: Nitrogen.moduleData.get(`${moduleId}`) } });
return Nitrogen.moduleData.get(`${moduleId}`);
}
getModuleDataFetcher(moduleId) {
return Nitrogen.moduleDataFetchers.get(`${moduleId}`);
}
async getRequestedData({ page, dynamicData, batchTransport, }) {
const dataModules = [];
function collectDataModules(modules) {
for (const module of modules) {
if (module.module) {
const moduleName = typeof module.module === 'object'
? module.module.name
: module.module;
if (Nitrogen.moduleDataFetchers.has(moduleName)) {
const updatedModule = updateModuleProps(module, dynamicData);
dataModules.push({
moduleId: module.id,
moduleName,
props: updatedModule.props,
});
}
}
if (module.props?.children) {
if (Array.isArray(module.props.children)) {
collectDataModules(module.props.children);
}
else if (typeof module.props.children === 'object') {
for (const slot in module.props.children) {
collectDataModules(module.props.children[slot]);
}
}
}
}
}
collectDataModules(page);
// Step 2: Create batch context if transport provided
const ctx = batchTransport
? new BatchFetchContext(batchTransport)
: undefined;
// Step 3: Start all getData calls concurrently
const promises = dataModules.map(({ moduleId, moduleName, props }) => {
const fetcher = Nitrogen.moduleDataFetchers.get(moduleName);
return fetcher({
...props,
__dynamicData: dynamicData,
}, ctx).then((data) => ({
moduleId,
data,
}));
});
// Step 4: If batch context exists, flush the single batch request
if (ctx) {
await ctx.flush();
}
// Step 5: Wait for all getData post-processing to complete
const results = await Promise.all(promises);
// Step 6: Store results for this request and update the shared module cache.
const requestedData = {};
for (const { moduleId, data } of results) {
requestedData[moduleId] = data;
Nitrogen.moduleData.set(`${moduleId}`, data);
}
return requestedData;
}
registerModuleDataFetcher(moduleId, getData) {
Nitrogen.moduleDataFetchers.set(`${moduleId}`, getData);
}
dispatchModulesEvent() {
if (typeof window === 'undefined')
return;
const dispatch = () => {
window.dispatchEvent(new CustomEvent('nitrogen-modules', {
detail: {
modules: Object.fromEntries(Nitrogen.modules),
},
}));
};
if (window?.nitrogen?.parentInit) {
dispatch();
return;
}
if (Nitrogen.nitrogenInitializedListenerAdded)
return;
Nitrogen.nitrogenInitializedListenerAdded = true;
window.addEventListener('nitrogen-initialized', () => {
Nitrogen.nitrogenInitializedListenerAdded = false;
dispatch();
}, { once: true });
}
queueModulesEvent() {
if (typeof window === 'undefined' || Nitrogen.moduleEventQueued)
return;
Nitrogen.moduleEventQueued = true;
const flush = () => {
Nitrogen.moduleEventQueued = false;
this.dispatchModulesEvent();
};
if (typeof queueMicrotask === 'function') {
queueMicrotask(flush);
}
else {
setTimeout(flush, 0);
}
}
registerModule(moduleId, Component, module, getData) {
Nitrogen.modules.set(`${moduleId}`, {
...module,
name: `${moduleId}`,
id: moduleId,
}); // overwriting the type so we can sneak in the id field
Nitrogen.moduleComponents.set(`${moduleId}`, Component);
if (getData) {
Nitrogen.moduleDataFetchers.set(`${moduleId}`, getData);
}
logger.log('Nitrogen.modules', {
context: { moduleId, moduleCount: Nitrogen.modules.size, windowType: typeof window },
});
this.queueModulesEvent();
}
}
export const nitrogen = new Nitrogen();