CartoDB/cartodb20

View on GitHub
webpack/v4/gearAwareResolver.js

Summary

Maintainability
B
5 hrs
Test Coverage
const { resolve, join, sep } = require('path');
const fs = require('fs');

// Detect existing gears
const gearPaths = [
  join(__dirname, '../../gears'),
  join(__dirname, '../../private_gears')
]
  .filter(gearPath => fs.existsSync(gearPath))
  .reduce((gears, gearsDir) => {
    fs.readdirSync(gearsDir).forEach((gearName) => { gears[gearName] = resolve(gearsDir, gearName); });
    return gears;
  }, {})
;

const fileCache = {};
const gearResolved = new Set();
const ROOT_DIR = resolve(__dirname, '../../');

function rootDir (file, opts = {}) {
  if (fileCache[file] === undefined || opts.skipCache) {
    // Try to find the file in gears
    for (let [gearName, gearPath] of Object.entries(gearPaths)) {
      let fileGearPath = join(gearPath, file);
      let fileExistsAndBoundedToGear =
          fs.existsSync(fileGearPath) &&
          fs.lstatSync(fileGearPath).isFile() &&
          // Check that the file is not from outside the gear path
          // PE, using ../..
          resolve(fileGearPath).startsWith(gearPath)
      ;

      // Try to look for full folder overriding, preceded by @
      if (!fileExistsAndBoundedToGear) {
        let pathParts = file.split(sep);

        pathParts.some((pathPart, pathPartIdx) => {
          fileGearPath = pathParts.slice(0, pathPartIdx).join(sep);

          if (fileGearPath) fileGearPath += sep;

          fileGearPath +=
            '@' + pathPart + sep +
            pathParts.slice(pathPartIdx + 1).join(sep)
          ;

          fileGearPath = join(gearPath, fileGearPath);

          fileExistsAndBoundedToGear =
              fs.existsSync(fileGearPath) &&
              fs.lstatSync(fileGearPath).isDirectory() &&
              // Check that the file is not from outside the gear path
              // PE, using ../..
              resolve(fileGearPath).startsWith(gearPath)
          ;

          return fileExistsAndBoundedToGear;
        });
      }

      if (fileExistsAndBoundedToGear) {
        fileCache[file] = {
          path: fileGearPath,
          gear: gearName
        };
        gearResolved.add(fileCache[file].path);
        break;
      }
    }

    // Find the file in project path if not in gear
    if (fileCache[file] === undefined) {
      fileCache[file] = {
        path: resolve(ROOT_DIR, file),
        gear: null
      };
      gearResolved.add(fileCache[file].path);
    }
  }

  file = fileCache[file];

  if (!opts.includeGear) {
    file = file.path;
  }

  return file;
}

// See https://github.com/webpack/webpack/blob/8a7597aa6eb2eef66a8f9db3a0c49bcb96022a94/lib/NormalModuleReplacementPlugin.js
// See https://webpack.js.org/plugins/normal-module-replacement-plugin/
class GearResolverPlugin {
  _resolve (result, attrName) {
    if (!result) return;

    if (/^!/.test(result[attrName])) return result;

    let path = result[attrName].startsWith('/')
      ? result[attrName]
      : join(result.context, result[attrName])
    ;

    let relPath = result.context
      ? path.replace(new RegExp('^' + ROOT_DIR + '/?'), '')
      : result[attrName]
    ;

    if (gearResolved.has(relPath)) {
      return result;
    } else {
      const file = rootDir(relPath, { includeGear: true });

      if (file.gear !== null) { // The file has been overridden by a gear
        result[attrName] = file.path;
      }

      return result;
    }
  }

  apply (compiler) {
    compiler.hooks.normalModuleFactory.tap(
      'GearResolverPlugin',
      nmf => {
        nmf.hooks.afterResolve.tap('NormalModuleReplacementPlugin', result => {
          if (!result) return;
          return this._resolve(result, 'resource');
        });
      })
    ;
  }
}

module.exports = {
  rootDir: rootDir,
  GearResolverPlugin: GearResolverPlugin
};