mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
116 lines
4.9 KiB
JavaScript
116 lines
4.9 KiB
JavaScript
/**
|
|
* Alpine.js CSP-safe lazy component loader.
|
|
*
|
|
* Scans the DOM for x-data attributes that match known lazy components.
|
|
* Only imports the JS modules for components actually present on the page.
|
|
* After all needed modules are loaded, calls Alpine.start().
|
|
*
|
|
* Dynamic import() is CSP-compliant (no eval / new Function).
|
|
*/
|
|
import Alpine from '@alpinejs/csp';
|
|
|
|
/**
|
|
* Map of Alpine component name → dynamic import function.
|
|
* Each module registers itself via Alpine.data() as a side effect.
|
|
*/
|
|
const lazyComponentMap = {
|
|
// --- Components only needed on specific pages ---
|
|
'adminSubmenu': () => import('./components/admin-submenu.js'),
|
|
'passwordToggle': () => import('./components/password-toggle.js'),
|
|
|
|
// --- Modal components (only on release pages) ---
|
|
'nfoModal': () => import('./components/nfo-modal.js'),
|
|
'filelistModal': () => import('./components/filelist-modal.js'),
|
|
'previewModal': () => import('./components/preview-modal.js'),
|
|
'mediainfoModal': () => import('./components/mediainfo-modal.js'),
|
|
'imageModal': () => import('./components/image-modal.js'),
|
|
|
|
// --- Page-specific components ---
|
|
'moviesPage': () => import('./components/movies-page.js'),
|
|
'qualityFilter': () => import('./components/quality-filter.js'),
|
|
'contentToggle': () => import('./components/content-toggle.js'),
|
|
'contentDelete': () => import('./components/content-toggle.js'), // same file
|
|
'releaseReport': () => import('./components/release-report.js'),
|
|
'adminReleaseReports': () => import('./components/release-report.js'), // same file
|
|
'profileEdit': () => import('./components/profile-edit.js'),
|
|
'profilePage': () => import('./components/profile-edit.js'), // same file
|
|
'copyToClipboard': () => import('./components/profile-edit.js'), // same file
|
|
'cartPage': () => import('./components/cart-page.js'),
|
|
'releaseMultiOps': () => import('./components/cart-page.js'), // same file
|
|
'authPage': () => import('./components/auth-page.js'),
|
|
'otpInput': () => import('./components/auth-page.js'), // same file
|
|
'passkeyLogin': () => import('./components/passkey-login.js'),
|
|
'passkeyManage': () => import('./components/passkey-manage.js'),
|
|
'adminUserPasskeys': () => import('./components/admin-user-passkeys.js'),
|
|
|
|
// --- Admin components (includes Chart.js — heavy) ---
|
|
'adminDashboard': () => import('./components/admin/dashboard.js'),
|
|
'adminGroups': () => import('./components/admin/groups.js'),
|
|
'adminFeatures': () => import('./components/admin/features.js'),
|
|
'adminUserEdit': () => import('./components/admin/features.js'), // same file
|
|
'adminUserList': () => import('./components/admin/features.js'), // same file
|
|
'adminDeletedUsers': () => import('./components/admin/features.js'), // same file
|
|
'adminInvitations': () => import('./components/admin/features.js'), // same file
|
|
'adminRegexForm': () => import('./components/admin/features.js'), // same file
|
|
'tinyMceEditor': () => import('./components/admin/features.js'), // same file
|
|
'verifyUser': () => import('./components/admin/features.js'), // same file
|
|
'tmuxEdit': () => import('./components/admin/features.js'), // same file
|
|
};
|
|
|
|
/**
|
|
* Extract Alpine component names from all x-data attributes in the DOM.
|
|
* Handles: x-data="componentName", x-data="componentName()", x-data="{ ... }" (skipped).
|
|
*/
|
|
function getUsedComponentNames() {
|
|
const names = new Set();
|
|
document.querySelectorAll('[x-data]').forEach(el => {
|
|
const raw = (el.getAttribute('x-data') || '').trim();
|
|
// Skip inline objects like { show: true }
|
|
if (!raw || raw.startsWith('{')) return;
|
|
// Extract identifier before ( or whitespace
|
|
const match = raw.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
if (match) names.add(match[1]);
|
|
});
|
|
return names;
|
|
}
|
|
|
|
/**
|
|
* Load only the lazy components found on the current page, then start Alpine.
|
|
*/
|
|
export function loadAndStart() {
|
|
const usedNames = getUsedComponentNames();
|
|
const imports = new Set(); // deduplicate (multiple names → same file)
|
|
|
|
for (const name of usedNames) {
|
|
if (lazyComponentMap[name]) {
|
|
imports.add(lazyComponentMap[name]);
|
|
}
|
|
}
|
|
|
|
if (imports.size === 0) {
|
|
Alpine.start();
|
|
enableTransitions();
|
|
return;
|
|
}
|
|
|
|
// Load all needed modules in parallel, then start Alpine
|
|
Promise.all([...imports].map(fn => fn()))
|
|
.then(() => {
|
|
Alpine.start();
|
|
enableTransitions();
|
|
})
|
|
.catch(err => {
|
|
console.error('[lazy-loader] Failed to load component:', err);
|
|
Alpine.start();
|
|
enableTransitions();
|
|
});
|
|
}
|
|
|
|
function enableTransitions() {
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
document.documentElement.removeAttribute('data-loading');
|
|
});
|
|
});
|
|
}
|