docusaurus-openai-search
Version:
AI-powered search plugin for Docusaurus - extends Algolia search with intelligent keyword generation and RAG-based answers
273 lines (272 loc) • 7.04 kB
JavaScript
/**
* Cleanup Manager Utility for Docusaurus AI Search
* Provides centralized cleanup mechanisms for DOM elements, refs, and resources
*/
export class CleanupManager {
constructor() {
this.tasks = new Map();
this.isDestroyed = false;
}
static getInstance() {
if (!CleanupManager.instance) {
CleanupManager.instance = new CleanupManager();
}
return CleanupManager.instance;
}
/**
* Reset the singleton instance (for testing/cleanup)
*/
static reset() {
if (CleanupManager.instance) {
CleanupManager.instance.cleanup();
CleanupManager.instance = null;
}
}
/**
* Register a cleanup task
*/
register(task) {
if (this.isDestroyed)
return;
this.tasks.set(task.id, task);
}
/**
* Unregister a cleanup task
*/
unregister(taskId) {
this.tasks.delete(taskId);
}
/**
* Execute cleanup for a specific task
*/
cleanupTask(taskId) {
const task = this.tasks.get(taskId);
if (task) {
try {
task.cleanup();
}
catch (error) {
console.error(`Cleanup failed for task ${taskId}:`, error);
}
this.tasks.delete(taskId);
}
}
/**
* Execute all cleanup tasks
*/
cleanup() {
if (this.isDestroyed)
return;
// Sort tasks by priority (higher priority first)
const sortedTasks = Array.from(this.tasks.values())
.sort((a, b) => (b.priority || 0) - (a.priority || 0));
// Execute cleanup tasks
for (const task of sortedTasks) {
try {
task.cleanup();
}
catch (error) {
console.error(`Cleanup failed for task ${task.id}:`, error);
}
}
this.tasks.clear();
this.isDestroyed = true;
}
/**
* Get number of registered tasks
*/
getTaskCount() {
return this.tasks.size;
}
/**
* Check if cleanup manager is destroyed
*/
isDestroyed_() {
return this.isDestroyed;
}
}
/**
* DOM Element Cleanup Utilities
*/
export class DOMCleanupUtils {
/**
* Clear all child nodes from an element
*/
static clearElement(element) {
if (!element)
return;
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
/**
* Remove element from DOM if it exists
*/
static removeElement(element) {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
}
/**
* Clear element attributes
*/
static clearAttributes(element, preserveAttributes = []) {
if (!element)
return;
const attributesToRemove = [];
// Collect attributes to remove
for (let i = 0; i < element.attributes.length; i++) {
const attr = element.attributes[i];
if (!preserveAttributes.includes(attr.name)) {
attributesToRemove.push(attr.name);
}
}
// Remove attributes
attributesToRemove.forEach(attrName => {
element.removeAttribute(attrName);
});
}
/**
* Reset element to initial state
*/
static resetElement(element, preserveAttributes = ['id', 'class']) {
if (!element)
return;
this.clearElement(element);
this.clearAttributes(element, preserveAttributes);
}
}
/**
* Ref Cleanup Utilities
*/
export class RefCleanupUtils {
/**
* Clear React ref
*/
static clearRef(ref) {
if (ref && 'current' in ref) {
ref.current = null;
}
}
/**
* Clear multiple refs of any type
*/
static clearRefs(...refs) {
refs.forEach(ref => this.clearRef(ref));
}
/**
* Reset ref to initial value
*/
static resetRef(ref, initialValue) {
if (ref && 'current' in ref) {
ref.current = initialValue;
}
}
}
/**
* Timer Cleanup Utilities
*/
export class TimerCleanupUtils {
/**
* Register a timer for cleanup
*/
static registerTimer(timer) {
this.timers.add(timer);
return timer;
}
/**
* Clear a specific timer
*/
static clearTimer(timer) {
clearTimeout(timer);
this.timers.delete(timer);
}
/**
* Clear all registered timers
*/
static clearAllTimers() {
this.timers.forEach(timer => clearTimeout(timer));
this.timers.clear();
}
/**
* Create a timeout that auto-registers for cleanup
*/
static setTimeout(callback, delay) {
const timer = setTimeout(() => {
callback();
this.timers.delete(timer);
}, delay);
return this.registerTimer(timer);
}
/**
* Create an interval that auto-registers for cleanup
*/
static setInterval(callback, delay) {
const timer = setInterval(callback, delay);
return this.registerTimer(timer);
}
}
TimerCleanupUtils.timers = new Set();
/**
* Modal-specific cleanup utilities
*/
export class ModalCleanupUtils {
/**
* Reset modal state
*/
static resetModalState(setState, initialState) {
setState(initialState);
}
/**
* Clear modal refs and DOM elements
*/
static clearModalRefs(...refs) {
RefCleanupUtils.clearRefs(...refs);
}
/**
* Complete modal cleanup
*/
static cleanupModal(options) {
const { refs = [], states = [], cleanupTasks = [] } = options;
// Clear refs
RefCleanupUtils.clearRefs(...refs);
// Reset states
states.forEach(({ setter, initialValue }) => {
setter(initialValue);
});
// Execute cleanup tasks
const cleanupManager = CleanupManager.getInstance();
cleanupTasks.forEach(taskId => {
cleanupManager.cleanupTask(taskId);
});
}
}
/**
* Hook for component cleanup
*/
export function useCleanup(componentId) {
const cleanupManager = CleanupManager.getInstance();
const registerCleanup = (taskId, cleanup, priority) => {
cleanupManager.register({
id: `${componentId}-${taskId}`,
cleanup,
priority
});
};
const unregisterCleanup = (taskId) => {
cleanupManager.unregister(`${componentId}-${taskId}`);
};
const cleanupComponent = () => {
// Find and cleanup all tasks for this component
const tasks = Array.from(cleanupManager['tasks'].entries())
.filter(([id]) => id.startsWith(`${componentId}-`));
tasks.forEach(([id]) => {
cleanupManager.cleanupTask(id);
});
};
return {
registerCleanup,
unregisterCleanup,
cleanupComponent
};
}