UNPKG

@nitrogenbuilder/client-core

Version:

Nitrogen Builder Core Client

149 lines (148 loc) 5.85 kB
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();