All files / lib dependencies.js

95.56% Statements 43/45
88.24% Branches 30/34
71.43% Functions 5/7
95.56% Lines 43/45

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    3x 3x 3x 3x 3x             14x 14x 14x 14x     6x   8x               607x 607x 14292x 807x 807x   807x       807x 744x 4467x 744x         14292x         616x 616x 616x   616x 425x   191x 191x 188x   3x       616x 607x   607x 244x     363x 744x 294x 294x 294x           372x     3x           456x              
'use strict';
 
const fs = require('fs');
const path = require('path');
const rawSourceCache = new Map();
const dependencySuffixes = /\.js|\.mjs|\.json/;
const dependencySuffixesRight = new RegExp(`(${dependencySuffixes.source})$`);
 
function escapeModuleSpecifier(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
 
function hasModuleSpecifier(source) {
  const realmImportValue = /importValue\((\s*)(('(.+)'|"(.*)"))(\s*),/g.exec(source);
  const dynamicImport = /import\((\s*)('(.+)'|"(.*)")(\s*)\)/g.exec(source);
  const moduleImport = /(import|from)(?:\s*)('(\.\/.*)'|"(\.\/.*)")/g.exec(source);
  if ((realmImportValue && realmImportValue.length) ||
      (dynamicImport && dynamicImport.length) ||
      (moduleImport && moduleImport.length)) {
    return true;
  }
  return false;
}
 
function findModuleSpecifiers(source) {
  // While it would be ideal to parse the source to an AST
  // and then visit each node to capture the ModuleSpecifier
  // strings, we can't be sure that all source code will
  // be parseable.
  let lines = source.split(/\r?\n/g);
  return lines.reduce((accum, line) => {
    if (dependencySuffixes.test(line)) {
      let parsedDynamicOrStaticImport = /(import|import\(|from)(\s*)('(.*?)'|"(.*?)")/g.exec(line);
      let parsedRealmImportValue = /importValue\((\s*)(('(.+)'|"(.*)"))(\s*),/g.exec(line);
 
      let parsed = parsedDynamicOrStaticImport && parsedDynamicOrStaticImport.length
        ? parsedDynamicOrStaticImport
        : parsedRealmImportValue;
 
      if (parsed && parsed.length) {
        for (let entry of parsed) {
          if (entry && dependencySuffixesRight.test(entry) && !accum.includes(entry)) {
            accum.push(entry.replace('./', ''));
          }
        }
      }
    }
    return accum;
  }, []);
}
 
function getDependencies(file, accum = []) {
  let dirname = path.dirname(file);
  let basename = path.basename(file);
  let contents = '';
 
  if (rawSourceCache.has(basename)) {
    contents = rawSourceCache.get(basename);
  } else {
    try {
      contents = fs.readFileSync(file, 'utf8');
      rawSourceCache.set(basename, contents);
    } catch (error) {
      accum.splice(accum.indexOf(basename), 1);
    }
  }
 
  if (contents.length) {
    let specifiers = findModuleSpecifiers(contents);
 
    if (!specifiers.length) {
      return [basename];
    }
 
    for (let specifier of specifiers) {
      if (!accum.includes(specifier)) {
        accum.push(specifier);
        Eif (basename !== specifier) {
          accum.push(...getDependencies(path.join(dirname, specifier), accum));
        }
      }
    }
  }
 
  return [...new Set(accum)];
}
 
module.exports = {
  escapeModuleSpecifier,
  getDependencies,
  hasModuleSpecifier,
  rawSource: {
    get(file) {
      return rawSourceCache.get(file);
    },
    clear() {
      rawSourceCache.clear();
    },
  },
};