You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.9 KiB
128 lines
3.9 KiB
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;
|
|
|