Skip to content

Commit

Permalink
[ENHANCEMENT] use EMBER_NODE_PATH to specify node_modules path
Browse files Browse the repository at this point in the history
ember-cli will use the value in env.EMBER_NODE_PATH if it is provided
to look up addons when `require`-ing them. This allows a user to
have their `node_modules` path outside of the project root.

Previously, the ember-cli custom require code assumed that
`node_modules` is always in the project's root.
  • Loading branch information
jakehow authored and mixonic committed Apr 7, 2015
1 parent 179b98c commit c5c3f8d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 28 deletions.
51 changes: 51 additions & 0 deletions lib/cli/node-modules-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

var path = require('path');

function isSubdirectoryOf(parentPath, possibleChildPath) {
return possibleChildPath.length > parentPath.length &&
possibleChildPath.indexOf(parentPath) === 0;
}

// A utility function for determining what path an addon may be found at. Addons
// will only be resolved in the project's local `node_modules/` directory. This
// is in contrast to a plain `require('some-module')` lookup, which would resolve
// a library according to the paths in NODE_PATH.
//
// A description of node's lookup logic can be found here:
//
// * https://nodejs.org/api/modules.html#modules_all_together
//
// By requiring with an absolute path this logic is bypassed.
//
module.exports = function nodeModulesPath(context) {

// Optionally configure a different home for `node_modules/` in a parent
// directory of the project. Possible use cases for this include caching
// the `node_modules/` directory outside of a source code checkout, and
// ensuring the same source code (shared over a network) can be used with
// different environments (Linux, OSX) where binary compatibility may not
// exist.
//
// For example, you can specify a different home directory for modules:
//
// EMBER_NODE_PATH=/opt/deps/node_modules NODE_PATH=/opt/deps/node_modules ember build
//
var nodePath = process.env.EMBER_NODE_PATH;
var contextPath = path.resolve(context);

if (nodePath) {
var configuredPath = path.resolve(nodePath);

// The contextPath is likely the project root, or possibly a subdirectory in
// node_modules/ nested dependencies. If it is more specific (a subdirectory
// of) than the configuredPath prefer the more specific contextPath.
if (isSubdirectoryOf(configuredPath, contextPath)) {
return path.resolve(contextPath,'node_modules');
} else {
return path.resolve(nodePath);
}
} else {
return path.resolve(contextPath,'node_modules');
}
};
17 changes: 9 additions & 8 deletions lib/models/addon-discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
@module ember-cli
*/

var assign = require('lodash/object/assign');
var debug = require('debug')('ember-cli:addon-discovery');
var fs = require('fs');
var path = require('path');
var CoreObject = require('core-object');
var resolve = require('resolve');
var findup = require('findup');
var assign = require('lodash/object/assign');
var debug = require('debug')('ember-cli:addon-discovery');
var fs = require('fs');
var path = require('path');
var CoreObject = require('core-object');
var resolve = require('resolve');
var findup = require('findup');
var nodeModulesPath = require ('../cli/node-modules-path');

/**
AddonDiscovery is responsible for collecting information about all of the
Expand Down Expand Up @@ -103,7 +104,7 @@ AddonDiscovery.prototype.discoverFromDependencies = function(root, pkg, excludeD

// this supports packages that do not have a valid entry point
// script (aka `main` entry in `package.json` or `index.js`)
addonPath = path.join(root, 'node_modules', name);
addonPath = path.join(nodeModulesPath(root), name);
var addon = discovery.discoverAtPath(addonPath);
if (addon) {
var chalk = require('chalk');
Expand Down
37 changes: 19 additions & 18 deletions lib/models/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@
/**
@module ember-cli
*/
var Promise = require('../ext/promise');
var path = require('path');
var findup = Promise.denodeify(require('findup'));
var resolve = Promise.denodeify(require('resolve'));
var fs = require('fs');
var find = require('lodash/collection/find');
var assign = require('lodash/object/assign');
var forOwn = require('lodash/object/forOwn');
var merge = require('lodash/object/merge');
var debug = require('debug')('ember-cli:project');
var AddonDiscovery = require('../models/addon-discovery');
var AddonsFactory = require('../models/addons-factory');
var Command = require('../models/command');
var UI = require('../ui');

var versionUtils = require('../utilities/version-utils');
var emberCLIVersion = versionUtils.emberCLIVersion;
var Promise = require('../ext/promise');
var path = require('path');
var findup = Promise.denodeify(require('findup'));
var resolve = Promise.denodeify(require('resolve'));
var fs = require('fs');
var find = require('lodash/collection/find');
var assign = require('lodash/object/assign');
var forOwn = require('lodash/object/forOwn');
var merge = require('lodash/object/merge');
var debug = require('debug')('ember-cli:project');
var AddonDiscovery = require('../models/addon-discovery');
var AddonsFactory = require('../models/addons-factory');
var Command = require('../models/command');
var UI = require('../ui');
var nodeModulesPath = require('../cli/node-modules-path');

var versionUtils = require('../utilities/version-utils');
var emberCLIVersion = versionUtils.emberCLIVersion;

/**
The Project model is tied to your package.json. It is instiantiated
Expand Down Expand Up @@ -216,7 +217,7 @@ Project.prototype.require = function(file) {
if (/^\.\//.test(file)) { // Starts with ./
return require(path.join(this.root, file));
} else {
return require(path.join(this.root, 'node_modules', file));
return require(path.join(nodeModulesPath(this.root), file));
}
};

Expand Down
5 changes: 3 additions & 2 deletions lib/utilities/require-local.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use strict';

var path = require('path');
var path = require('path');
var nodeModulesPath = require('../cli/node-modules-path');

module.exports = function requireLocal(lib) {
return require(path.join(process.cwd(), 'node_modules', lib));
return require(path.join(nodeModulesPath(process.cwd()), lib));
};
43 changes: 43 additions & 0 deletions tests/unit/cli/node-modules-path-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';
/*jshint expr: true*/

var expect = require('chai').expect;
var path = require('path');
var nodeModulesPath = require('../../../lib/cli/node-modules-path');

describe('cli/node-module-path.js', function() {

afterEach(function() {
delete process.env.EMBER_NODE_PATH;
});


it('nodeModulesPath() should return the local node_modules path by default.', function() {
// Valid commands
var expectedPath = path.join(process.cwd(),'node_modules');

expect(
nodeModulesPath(process.cwd())
).to.equal(expectedPath);
});

it('nodeModulesPath() should return subdirectories of EMBER_NODE_PATH when set to an absolute path.', function() {
if (process.platform === 'win32') {
process.env.EMBER_NODE_PATH = 'C:\\tmp\node_modules';
} else {
process.env.EMBER_NODE_PATH = '/tmp/node_modules';
}

expect(nodeModulesPath(process.cwd())).to.equal(path.resolve(process.env.EMBER_NODE_PATH));

var addOnPath = path.resolve(process.env.EMBER_NODE_PATH, 'node_modules', 'my-add-on');
var addOnModulesPath = path.resolve(process.env.EMBER_NODE_PATH, 'node_modules', 'my-add-on', 'node_modules');
expect(nodeModulesPath(addOnPath)).to.equal(addOnModulesPath);
});

it('nodeModulesPath() should return subdirectories of EMBER_NODE_PATH when set to a relative path.', function() {
process.env.EMBER_NODE_PATH = '../../tmp/node_modules';
expect(nodeModulesPath(process.cwd())).to.equal(path.resolve('../../tmp','node_modules'));
expect(nodeModulesPath('../../tmp/node_modules/my-add-on')).to.equal(path.resolve('../../tmp','node_modules','my-add-on','node_modules'));
});
});

0 comments on commit c5c3f8d

Please sign in to comment.