├── Makefile ├── test ├── fixtures │ └── lib │ │ └── node_modules │ │ └── beaker │ │ ├── index.js │ │ └── package.json └── requiregSpec.js ├── .gitignore ├── .travis.yml ├── lib ├── requireg.js └── resolvers.js ├── LICENSE ├── package.json └── README.md /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | npm test 3 | 4 | .PHONY: test 5 | -------------------------------------------------------------------------------- /test/fixtures/lib/node_modules/beaker/index.js: -------------------------------------------------------------------------------- 1 | module.exports = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | npm-debug.log 3 | .DS_Store 4 | .idea/ 5 | Thumbs.db -------------------------------------------------------------------------------- /test/fixtures/lib/node_modules/beaker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beaker", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | - 10 6 | - 9 7 | - 8 8 | - 7 9 | - 6 10 | - 5 11 | - 4 12 | 13 | script: make test 14 | -------------------------------------------------------------------------------- /lib/requireg.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var resolvers = require('./resolvers') 4 | var NestedError = require('nested-error-stacks') 5 | 6 | 'use strict'; 7 | 8 | module.exports = requireg 9 | 10 | function requireg(module, onlyGlobal) { 11 | try { 12 | return require(resolve(module, undefined, onlyGlobal)) 13 | } catch (e) { 14 | throw new NestedError("Could not require module '"+ module +"'", e) 15 | } 16 | } 17 | 18 | requireg.resolve = resolve 19 | 20 | requireg.globalize = function () { 21 | global.requireg = requireg 22 | } 23 | 24 | function resolve(module, dirname, onlyGlobal) { 25 | var i, l, resolver, modulePath 26 | 27 | for (i = (onlyGlobal ? 1 : 0), l = resolvers.length; i < l; i += 1) { 28 | resolver = resolvers[i] 29 | if (modulePath = resolver(module, dirname)) { 30 | break 31 | } 32 | } 33 | 34 | return modulePath 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Tomas Aparicio 2 | All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "requireg", 3 | "version": "0.2.2", 4 | "description": "Require and resolve global modules like a boss", 5 | "homepage": "http://github.com/h2non/requireg", 6 | "bugs": "https://github.com/h2non/requireg/issues", 7 | "contributors": [ 8 | { 9 | "name": "Tomas Aparicio", 10 | "email": "tomas@aparicio.me" 11 | }, 12 | { 13 | "name": "Eugene Sharygin", 14 | "url": "https://github.com/eush77" 15 | } 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/h2non/requireg.git" 20 | }, 21 | "licenses": "MIT", 22 | "main": "lib/requireg", 23 | "directories": { 24 | "lib": "./lib" 25 | }, 26 | "engines": { 27 | "node": ">= 4.0.0" 28 | }, 29 | "scripts": { 30 | "test": "mocha -u tdd --ui exports --reporter spec --slow 2000ms --bail" 31 | }, 32 | "keywords": [ 33 | "global", 34 | "npm", 35 | "modules", 36 | "module", 37 | "require", 38 | "import", 39 | "resolve" 40 | ], 41 | "dependencies": { 42 | "nested-error-stacks": "~2.0.1", 43 | "rc": "~1.2.7", 44 | "resolve": "~1.7.1" 45 | }, 46 | "devDependencies": { 47 | "mocha": "~5.2.0", 48 | "expect.js": "~0.3.1", 49 | "rewire": "~4.0.1", 50 | "semver": "~5.5.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # requireg [![Build Status](https://secure.travis-ci.org/h2non/requireg.png?branch=master)][2] [![NPM version](https://badge.fury.io/js/requireg.png)][3] 2 | 3 | Require and resolve global modules in node.js like a boss. 4 | 5 | ## Differences with require() 6 | 7 | `requireg` tries to find modules in global locations which are 8 | not natively supported by the node.js [module resolve algorithm][1]. 9 | 10 | It supports both npm/yarn global packages installation paths. 11 | 12 | Supported locations: 13 | 14 | - $HOME/node_modules (instead of $HOME/.node_modules) 15 | - $HOME/node_libraries (instead of $HOME/.node_libraries) 16 | - $HOME/node_packages (specific of `requireg`) 17 | - $PREFIX/lib/node_modules (instead of $PREFIX/lib/node) 18 | - $NODE_MODULES (use the specific modules path environment variable) 19 | 20 | ## Resolution priority 21 | 22 | 1. Resolve via native `require()` (unless second parameter is true) 23 | 2. User home directory (`$HOME` or `%USERPROFILE%`) 24 | 3. Node installation path 25 | 4. $NODE_MODULES (can have different multiple paths, semicolon separated) 26 | 5. Common operative system installation paths 27 | 28 | ## Installation 29 | 30 | ```bash 31 | $ npm install requireg --save[-dev] 32 | ``` 33 | 34 | ```bash 35 | $ yarn add requireg 36 | ``` 37 | 38 | ## API 39 | 40 | ### requireg(path: string, onlyGlobal: boolean = false) 41 | 42 | ## Usage 43 | 44 | ### Load global modules 45 | 46 | ```js 47 | var requireg = require('requireg') 48 | // require a globally installed package 49 | var npm = requireg('npm') 50 | ``` 51 | 52 | ### Load only global modules 53 | 54 | ```js 55 | var requireg = require('requireg') 56 | // require a globally installed package and skip local packages 57 | var eslint = requireg('eslint', true) 58 | ``` 59 | 60 | ### Resolve module path 61 | 62 | ```js 63 | var modulePath = requireg.resolve('npm') 64 | // returns '/usr/local/lib/node_modules/npm/lib/npm.js' 65 | ``` 66 | 67 | ### Globalize it 68 | 69 | ```js 70 | require('requireg').globalize() 71 | ``` 72 | 73 | Now it is globally available from any source file 74 | 75 | ```js 76 | var globalModule = requireg('npm') 77 | ``` 78 | 79 | ### Module not found 80 | 81 | `requireg` maintains the same behavior as the native `require()`. 82 | It will throw an `Error` exception if the module was not found 83 | 84 | ## Considerations 85 | 86 | - Require global modules in node.js is considered anti-pattern. 87 | Note that you can experiment unreliability or inconsistency across different environments. 88 | I hope you know exactly what you do with `requireg` 89 | - Only node packages installed with [npm](https://npmjs.org) or [yarn](https://yarnpkg.com) are supported (which means only standardized NPM paths are supported) 90 | 91 | ## Possible extra features 92 | 93 | - Custom environment variable with custom path to resolve global modules. 94 | 95 | ## License 96 | 97 | Released under MIT license 98 | 99 | [1]: http://nodejs.org/docs/latest/api/modules.html#modules_all_together 100 | [2]: http://travis-ci.org/h2non/requireg 101 | [3]: http://badge.fury.io/js/requireg 102 | -------------------------------------------------------------------------------- /lib/resolvers.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var resolve = require('resolve').sync 4 | var rc = require('rc') 5 | var spawnSync = require('child_process').spawnSync 6 | var isWin32 = process.platform === 'win32' 7 | 8 | 'use strict'; 9 | 10 | // resolvers 11 | module.exports = [ 12 | nativeResolve, 13 | nodePathResolve, 14 | userHomeResolve, 15 | nodeModulesResolve, 16 | yarnModulesResolve, 17 | prefixResolve, 18 | execPathResolve 19 | ] 20 | 21 | function resolveFn(module, basePath, dirname) { 22 | try { 23 | return resolve(module, { 24 | basedir: path.join(basePath, dirname || '') 25 | }) 26 | } catch (e) {} 27 | } 28 | 29 | // resolve using native require() function 30 | // if NODE_PATH is defined, a global module should be natively resolved 31 | function nativeResolve(module, dirname) { 32 | try { 33 | return require.resolve(module, dirname) 34 | } catch (e) {} 35 | } 36 | 37 | // See: http://nodejs.org/docs/latest/api/modules.html#modules_loading_from_the_global_folders 38 | // required? 39 | function nodePathResolve(module, dirname) { 40 | var i, l, modulePath 41 | var nodePath = process.env.NODE_PATH 42 | 43 | if (!nodePath) { return } 44 | 45 | nodePath = nodePath.split(path.delimiter).map(function (nodepath) { 46 | return path.normalize(nodepath) 47 | }) 48 | 49 | for (i = 0, l = nodePath.length; i < l; i += 1) { 50 | if (modulePath = resolveFn(module, dirname || nodePath[i])) { 51 | break 52 | } 53 | } 54 | 55 | return modulePath 56 | } 57 | 58 | function userHomeResolve(module) { 59 | var i, l, modulePath 60 | var homePath = isWin32 ? process.env['USERPROFILE'] : process.env['HOME'] 61 | 62 | var paths = [ 63 | 'node_modules', 64 | 'node_libraries', 65 | 'node_packages' 66 | ] 67 | 68 | for (i = 0, l = paths.length; i < l; i += 1) { 69 | if (modulePath = resolveFn(module, homePath, paths[i])) { 70 | break; 71 | } 72 | } 73 | 74 | return modulePath 75 | } 76 | 77 | // See: https://npmjs.org/doc/files/npm-folders.html#prefix-Configuration 78 | // it uses execPath to discover the default prefix on *nix and %APPDATA% on Windows 79 | function prefixResolve(module) { 80 | var modulePath, dirname 81 | var prefix = rc('npm').prefix 82 | 83 | if (isWin32) { 84 | prefix = prefix || path.join(process.env.APPDATA, 'npm') 85 | dirname = prefix 86 | } 87 | else { 88 | prefix = prefix || path.join(path.dirname(process.execPath), '..') 89 | dirname = path.join(prefix, 'lib') 90 | } 91 | 92 | dirname = path.join(dirname, 'node_modules') 93 | modulePath = resolveFn(module, dirname) 94 | 95 | return modulePath 96 | } 97 | 98 | // Resolves packages using the node installation path 99 | // Useful for resolving global packages such as npm when the prefix has been overriden by the user 100 | function execPathResolve(module) { 101 | var modulePath, dirname 102 | var execPath = path.dirname(process.execPath) 103 | 104 | if (isWin32) { 105 | dirname = execPath 106 | } 107 | else { 108 | dirname = path.join(execPath, '..', 'lib') 109 | } 110 | 111 | dirname = path.join(dirname, 'node_modules') 112 | modulePath = resolveFn(module, dirname) 113 | 114 | return modulePath 115 | } 116 | 117 | function nodeModulesResolve(module) { 118 | var i, l, modulePath 119 | var nodeModules = process.env['NODE_MODULES'] 120 | 121 | if (typeof nodeModules === 'string') { 122 | nodeModules = nodeModules.split(path.delimiter) 123 | for (i = 0, l = nodeModules.length; i < l; i += 1) { 124 | if (modulePath = resolveFn(module, nodeModules[i])) { 125 | break; 126 | } 127 | } 128 | } 129 | 130 | return modulePath 131 | } 132 | 133 | function yarnModulesResolve(module) { 134 | var i, modulePath; 135 | 136 | // Retrieve yarn global path 137 | var yarnCmd = isWin32 ? 'yarn.cmd' : 'yarn'; 138 | var result = spawnSync(yarnCmd, ['global', 'dir'], { encoding: 'utf8' }); 139 | 140 | if (!result.error && result.stdout) { 141 | var yarnPath = result.stdout.replace(/[\r\n]+/g, ''); 142 | 143 | var nodeModulesStr = path.join(yarnPath, 'node_modules'); 144 | if (typeof nodeModulesStr === 'string') { 145 | var nodeModules = nodeModulesStr.split(path.delimiter); 146 | for (i = 0; i < nodeModules.length; i++) { 147 | if (modulePath = resolveFn(module, nodeModules[i])) { 148 | break; 149 | } 150 | } 151 | } 152 | } 153 | 154 | return modulePath; 155 | } 156 | -------------------------------------------------------------------------------- /test/requiregSpec.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | var expect = require('expect.js') 4 | var resolvers = require('rewire')('../lib/resolvers') 5 | require.cache[require.resolve('../lib/resolvers')] = { exports: resolvers } 6 | var requiregModule = require('../lib/requireg') 7 | 8 | var isWin32 = process.platform === 'win32' 9 | var homeVar = isWin32 ? 'USERPROFILE' : 'HOME' 10 | var homePath = process.env[homeVar] 11 | 12 | describe('requireg', function () { 13 | 14 | it('should be a function', function () { 15 | expect(requiregModule).to.be.a('function') 16 | }) 17 | 18 | describe('requireg API', function () { 19 | 20 | it('should globalize', function () { 21 | requiregModule.globalize() 22 | expect(requireg).to.be.a('function') 23 | }) 24 | 25 | }) 26 | 27 | describe('local modules', function () { 28 | 29 | it('should resolve a local module', function () { 30 | expect(requiregModule('expect.js')).to.be.equal(expect) 31 | }) 32 | 33 | it('should throw an Error exception when no local module exists', function () { 34 | expect(function () { requiregModule('nonexistent') }).to.throwError() 35 | }) 36 | 37 | }) 38 | 39 | describe('global modules', function () { 40 | 41 | describe('resolve only global', function () { 42 | 43 | it('should not resolve a local module', function () { 44 | expect(function () { requiregModule('expect.js', true) }).to.throwError() 45 | }) 46 | 47 | }) 48 | 49 | describe('resolve via NODE_PATH', function () { 50 | 51 | before(function () { 52 | process.env.NODE_PATH = path.join(__dirname, 'fixtures', 'lib'); 53 | }) 54 | 55 | after(function () { 56 | process.env.NODE_PATH = '' 57 | }) 58 | 59 | it('should resolve the beaker package', function () { 60 | expect(requiregModule('beaker')).to.be.true 61 | }) 62 | 63 | it('should have the expected module path', function () { 64 | expect(requiregModule.resolve('beaker')) 65 | .to.be.equal(path.join(__dirname, 'fixtures', 'lib', 'node_modules', 'beaker', 'index.js')) 66 | }) 67 | 68 | }) 69 | 70 | describe('resolve via $HOME', function () { 71 | 72 | before(function () { 73 | process.env[homeVar] = path.join(__dirname, 'fixtures', 'lib') 74 | }) 75 | 76 | after(function () { 77 | process.env[homeVar] = homePath 78 | }) 79 | 80 | it('should resolve the beaker package', function () { 81 | expect(requiregModule('beaker')).to.be.true 82 | }) 83 | 84 | }) 85 | 86 | describe('resolve via $NODE_MODULES', function () { 87 | 88 | before(function () { 89 | process.env.NODE_MODULES = path.join(__dirname, 'fixtures', 'lib') 90 | }) 91 | 92 | after(function () { 93 | process.env.NODE_MODULES = '' 94 | }) 95 | 96 | it('should resolve the beaker package', function () { 97 | expect(requiregModule('beaker')).to.be.true 98 | }) 99 | 100 | }) 101 | 102 | describe('resolve via node execution path', function () { 103 | var execPath = process.execPath 104 | var rc = require('rc') 105 | 106 | before(function () { 107 | process.execPath = path.join(__dirname, 'fixtures', (isWin32 ? 'lib' : 'bin'), 'node') 108 | }) 109 | 110 | after(function () { 111 | process.execPath = execPath 112 | }) 113 | 114 | it('should resolve the beaker package', function () { 115 | expect(requiregModule('beaker')).to.be.true 116 | }) 117 | 118 | it('should have the expected module path', function () { 119 | expect(requiregModule.resolve('beaker')) 120 | .to.be.equal(path.join(__dirname, 'fixtures', 'lib', 'node_modules', 'beaker', 'index.js')) 121 | }) 122 | 123 | }) 124 | 125 | describe('resolve via npm prefix', function () { 126 | var rc = require('rc') 127 | 128 | before(function () { 129 | resolvers.__set__('rc', function () { 130 | return { 131 | prefix: path.join(__dirname, 'fixtures', (isWin32 ? 'lib' : '')) 132 | } 133 | }) 134 | }) 135 | 136 | after(function () { 137 | resolvers.__set__('rc', rc) 138 | }) 139 | 140 | it('should resolve the beaker package', function () { 141 | expect(requiregModule('beaker')).to.be.true 142 | }) 143 | 144 | it('should have the expected module path', function () { 145 | expect(requiregModule.resolve('beaker')) 146 | .to.be.equal(path.join(__dirname, 'fixtures', 'lib', 'node_modules', 'beaker', 'index.js')) 147 | }) 148 | 149 | }) 150 | 151 | }) 152 | 153 | }) 154 | --------------------------------------------------------------------------------