summaryrefslogtreecommitdiffstats
path: root/node_modules/require_optional/index.js
blob: 3710319feb36beef46f85dbfb9efbb5f203d55d3 (plain) (blame)
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
122
123
124
125
126
127
128
var path = require('path'),
  fs = require('fs'),
  f = require('util').format,
  resolveFrom = require('resolve-from'),
  semver = require('semver');

var exists = fs.existsSync || path.existsSync;

// Find the location of a package.json file near or above the given location
var find_package_json = function(location) {
  var found = false;

  while(!found) {
    if (exists(location + '/package.json')) {
      found = location;
    } else if (location !== '/') {
      location = path.dirname(location);
    } else {
      return false;
    }
  }

  return location;
}

// Find the package.json object of the module closest up the module call tree that contains name in that module's peerOptionalDependencies
var find_package_json_with_name = function(name) {
  // Walk up the module call tree until we find a module containing name in its peerOptionalDependencies
  var currentModule = module;
  var found = false;
  while (currentModule) {
    // Check currentModule has a package.json
    location = currentModule.filename;
    var location = find_package_json(location)
    if (!location) {
      currentModule = currentModule.parent;
      continue;
    }

    // Read the package.json file
    var object = JSON.parse(fs.readFileSync(f('%s/package.json', location)));
    // Is the name defined by interal file references
    var parts = name.split(/\//);

    // Check whether this package.json contains peerOptionalDependencies containing the name we're searching for
    if (!object.peerOptionalDependencies || (object.peerOptionalDependencies && !object.peerOptionalDependencies[parts[0]])) {
      currentModule = currentModule.parent;
      continue;
    }
    found = true;
    break;
  }

  // Check whether name has been found in currentModule's peerOptionalDependencies
  if (!found) {
    throw new Error(f('no optional dependency [%s] defined in peerOptionalDependencies in any package.json', parts[0]));
  }

  return {
    object: object,
    parts: parts
  }
}

var require_optional = function(name, options) {
  options = options || {};
  options.strict = typeof options.strict == 'boolean' ? options.strict : true;

  var res = find_package_json_with_name(name)
  var object = res.object;
  var parts = res.parts;

  // Unpack the expected version
  var expectedVersions = object.peerOptionalDependencies[parts[0]];
  // The resolved package
  var moduleEntry = undefined;
  // Module file
  var moduleEntryFile = name;

  try {
    // Validate if it's possible to read the module
    moduleEntry = require(moduleEntryFile);
  } catch(err) {
    // Attempt to resolve in top level package
    try {
      // Get the module entry file
      moduleEntryFile = resolveFrom(process.cwd(), name);
      if(moduleEntryFile == null) return undefined;
      // Attempt to resolve the module
      moduleEntry = require(moduleEntryFile);
    } catch(err) {
      if(err.code === 'MODULE_NOT_FOUND') return undefined;
    }
  }

  // Resolve the location of the module's package.json file
  var location = find_package_json(require.resolve(moduleEntryFile));
  if(!location) {
    throw new Error('package.json can not be located');
  }

  // Read the module file
  var dependentOnModule = JSON.parse(fs.readFileSync(f('%s/package.json', location)));
  // Get the version
  var version = dependentOnModule.version;
  // Validate if the found module satisfies the version id
  if(semver.satisfies(version, expectedVersions) == false
    && options.strict) {
      var error = new Error(f('optional dependency [%s] found but version [%s] did not satisfy constraint [%s]', parts[0], version, expectedVersions));
      error.code = 'OPTIONAL_MODULE_NOT_FOUND';
      throw error;
  }

  // Satifies the module requirement
  return moduleEntry;
}

require_optional.exists = function(name) {
  try {
    var m = require_optional(name);
    if(m === undefined) return false;
    return true;
  } catch(err) {
    return false;
  }
}

module.exports = require_optional;