Resource Loading Optimization

Resource Loading Optimization

CSP can impact resource loading performance. Here's how to optimize:

// Resource Loading Optimizer
class CSPResourceOptimizer {
  constructor() {
    this.resourcePatterns = new Map();
    this.loadingStrategies = this.defineLoadingStrategies();
  }
  
  defineLoadingStrategies() {
    return {
      scripts: {
        preload: true,
        async: true,
        defer: true,
        bundling: true,
        recommendations: [
          'Use resource hints with CSP-compliant sources',
          'Bundle first-party scripts to reduce CSP checks',
          'Implement progressive loading for non-critical scripts'
        ]
      },
      
      styles: {
        critical: true,
        preload: true,
        bundling: true,
        recommendations: [
          'Inline critical CSS with nonces',
          'Preload external stylesheets',
          'Use CSS-in-JS with CSP support'
        ]
      },
      
      images: {
        lazy: true,
        responsive: true,
        optimization: true,
        recommendations: [
          'Implement lazy loading for below-fold images',
          'Use srcset for responsive images',
          'Optimize image formats and compression'
        ]
      }
    };
  }
  
  optimizeResourceLoading() {
    return `
      // CSP-aware resource loader
      class CSPResourceLoader {
        constructor(cspNonce) {
          this.nonce = cspNonce;
          this.loadedResources = new Set();
          this.pendingResources = new Map();
        }
        
        // Optimized script loading with CSP
        async loadScript(src, options = {}) {
          // Check if already loaded
          if (this.loadedResources.has(src)) {
            return Promise.resolve();
          }
          
          // Check if loading in progress
          if (this.pendingResources.has(src)) {
            return this.pendingResources.get(src);
          }
          
          const loadPromise = new Promise((resolve, reject) => {
            const script = document.createElement('script');
            
            // CSP compliance
            if (this.nonce) {
              script.nonce = this.nonce;
            }
            
            // Performance optimizations
            script.async = options.async !== false;
            script.defer = options.defer || false;
            
            // Resource hints
            if (options.preload) {
              this.preloadResource(src, 'script');
            }
            
            script.onload = () => {
              this.loadedResources.add(src);
              this.pendingResources.delete(src);
              resolve();
            };
            
            script.onerror = () => {
              this.pendingResources.delete(src);
              reject(new Error(\`Failed to load script: \${src}\`));
            };
            
            script.src = src;
            document.head.appendChild(script);
          });
          
          this.pendingResources.set(src, loadPromise);
          return loadPromise;
        }
        
        // Preload resources with CSP compliance
        preloadResource(href, as) {
          const link = document.createElement('link');
          link.rel = 'preload';
          link.as = as;
          link.href = href;
          
          if (as === 'script' || as === 'style') {
            link.nonce = this.nonce;
          }
          
          document.head.appendChild(link);
        }
        
        // Bundle loader for reduced CSP overhead
        async loadBundle(resources) {
          const results = await Promise.allSettled(
            resources.map(resource => {
              if (typeof resource === 'string') {
                return this.loadScript(resource);
              } else {
                return this.loadScript(resource.src, resource.options);
              }
            })
          );
          
          const failed = results.filter(r => r.status === 'rejected');
          if (failed.length > 0) {
            console.warn('Some resources failed to load:', failed);
          }
          
          return results;
        }
      }
    `;
  }
  
  implementProgressiveEnhancement() {
    return {
      criticalPath: `
        // Load critical resources first
        (async function() {
          const loader = new CSPResourceLoader(window.__CSP_NONCE__);
          
          // Critical resources
          await loader.loadBundle([
            { src: '/js/core.js', options: { preload: true } },
            { src: '/js/critical-path.js', options: { async: false } }
          ]);
          
          // Non-critical resources
          requestIdleCallback(() => {
            loader.loadBundle([
              '/js/analytics.js',
              '/js/social-widgets.js',
              '/js/enhancement.js'
            ]);
          });
        })();
      `,
      
      lazyLoading: `
        // Intersection Observer for lazy loading with CSP
        class CSPLazyLoader {
          constructor(nonce) {
            this.nonce = nonce;
            this.observer = this.createObserver();
          }
          
          createObserver() {
            return new IntersectionObserver((entries) => {
              entries.forEach(entry => {
                if (entry.isIntersecting) {
                  this.loadResource(entry.target);
                  this.observer.unobserve(entry.target);
                }
              });
            }, {
              rootMargin: '50px'
            });
          }
          
          loadResource(element) {
            if (element.dataset.src) {
              if (element.tagName === 'SCRIPT') {
                const script = document.createElement('script');
                script.src = element.dataset.src;
                script.nonce = this.nonce;
                element.parentNode.replaceChild(script, element);
              } else if (element.tagName === 'IMG') {
                element.src = element.dataset.src;
                element.removeAttribute('data-src');
              }
            }
          }
          
          observe(selector) {
            document.querySelectorAll(selector).forEach(el => {
              this.observer.observe(el);
            });
          }
        }
      `
    };
  }
}