All files / src/utils dependency-utils.js

35.45% Statements 39/110
100% Branches 0/0
0% Functions 0/5
35.45% Lines 39/110

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 1221x 1x 1x   1x 1x 1x 1x 1x 1x                   1x 1x 1x 1x 1x 1x 1x 1x                         1x 1x 1x 1x 1x 1x 1x 1x                                               1x 1x 1x 1x 1x 1x                                                       1x 1x 1x 1x 1x 1x 1x 1x                      
/**
 * Utility functions for handling optional peer dependencies
 */
 
/**
 * Check if a peer dependency is available
 * @param {string} packageName - Name of the package to check
 * @returns {boolean} - Whether the package is available
 */
export function isPeerDependencyAvailable(packageName) {
  try {
    // Try to resolve the package
    import.meta.resolve(packageName);
    return true;
  } catch {
    return false;
  }
}
 
/**
 * Dynamically import a peer dependency with error handling
 * @param {string} packageName - Name of the package to import
 * @param {string} integrationName - Human-readable name of the integration
 * @returns {Promise<any>} - The imported module
 * @throws {Error} - If the dependency is not available
 */
export async function importPeerDependency(packageName, integrationName) {
  try {
    return await import(packageName);
  } catch {
    throw new Error(
      `${integrationName} integration requires the '${packageName}' package to be installed.\n` +
      `Please install it with: npm install ${packageName}\n` +
      `Or with pnpm: pnpm add ${packageName}\n` +
      `Or with yarn: yarn add ${packageName}`
    );
  }
}
 
/**
 * Create a lazy-loaded integration that only imports dependencies when needed
 * @param {string} packageName - Name of the package to import
 * @param {string} integrationName - Human-readable name of the integration
 * @param {Function} createIntegration - Function that creates the integration using the imported package
 * @returns {Function} - Lazy-loaded integration function
 */
export function createLazyIntegration(packageName, integrationName, createIntegration) {
  let cachedIntegration = null;
  let importPromise = null;
 
  return async function(...args) {
    // Return cached integration if available
    if (cachedIntegration) {
      return cachedIntegration(...args);
    }
 
    // Avoid multiple concurrent imports
    if (!importPromise) {
      importPromise = importPeerDependency(packageName, integrationName)
        .then(module => {
          cachedIntegration = createIntegration(module);
          return cachedIntegration;
        });
    }
 
    const integration = await importPromise;
    return integration(...args);
  };
}
 
/**
 * Check multiple peer dependencies and provide helpful error messages
 * @param {Array<{package: string, integration: string}>} dependencies - List of dependencies to check
 * @returns {Object} - Object with availability status for each dependency
 */
export function checkPeerDependencies(dependencies) {
  const results = {};
  const missing = [];
 
  for (const { package: packageName, integration } of dependencies) {
    const available = isPeerDependencyAvailable(packageName);
    results[packageName] = available;
    
    if (!available) {
      missing.push({ package: packageName, integration });
    }
  }
 
  if (missing.length > 0) {
    const installCommands = missing.map(({ package: pkg }) => pkg).join(' ');
    const integrationsList = missing.map(({ integration }) => integration).join(', ');
    
    console.warn(
      `Optional dependencies missing for ${integrationsList} integration(s).\n` +
      `To use these integrations, install: npm install ${installCommands}\n` +
      `Or with pnpm: pnpm add ${installCommands}\n` +
      `Or with yarn: yarn add ${installCommands}`
    );
  }
 
  return results;
}
 
/**
 * Create a function that checks for a dependency before executing
 * @param {string} packageName - Name of the package required
 * @param {string} integrationName - Human-readable name of the integration
 * @param {Function} fn - Function to execute if dependency is available
 * @returns {Function} - Wrapped function with dependency check
 */
export function requirePeerDependency(packageName, integrationName, fn) {
  return function(...args) {
    if (!isPeerDependencyAvailable(packageName)) {
      throw new Error(
        `${integrationName} integration requires the '${packageName}' package to be installed.\n` +
        `Please install it with: npm install ${packageName}`
      );
    }
    return fn.apply(this, args);
  };
}