diff --git a/lib/grunt/task.js b/lib/grunt/task.js index 48aec3abf..ffd119eb0 100644 --- a/lib/grunt/task.js +++ b/lib/grunt/task.js @@ -370,7 +370,23 @@ task.loadTasks = function(tasksdir) { task.loadNpmTasks = function(name) { loadTasksMessage('"' + name + '" local Npm module'); var root = path.resolve('node_modules'); - var pkgfile = path.join(root, name, 'package.json'); + var pkgpath = path.join(root, name); + var pkgfile = path.join(pkgpath, 'package.json'); + // If package does not exist where grunt expects it to be, + // try to find it using Node's package path resolution mechanism + if (!grunt.file.exists(pkgpath)) { + var nameParts = name.split('/'); + // In case name points to directory inside module, + // get real name of the module with respect to scope (if any) + var normailzedName = (name[0] === '@' ? nameParts.slice(0,2).join('/') : nameParts[0]); + try { + pkgfile = require.resolve(normailzedName + '/package.json'); + root = pkgfile.substr(0, pkgfile.length - normailzedName.length - '/package.json'.length); + } catch (err) { + grunt.log.error('Local Npm module "' + normailzedName + '" not found. Is it installed?'); + return; + } + } var pkg = grunt.file.exists(pkgfile) ? grunt.file.readJSON(pkgfile) : {keywords: []}; // Process collection plugins. diff --git a/test/fixtures/load-npm-tasks/test-package/package.json b/test/fixtures/load-npm-tasks/test-package/package.json new file mode 100644 index 000000000..8de3ba72d --- /dev/null +++ b/test/fixtures/load-npm-tasks/test-package/package.json @@ -0,0 +1,7 @@ +{ + "private": true, + "name": "test-package", + "devDependencies": { + "grunt-foo-plugin": "1.0.0" + } +} \ No newline at end of file diff --git a/test/gruntfile/load-npm-tasks.js b/test/gruntfile/load-npm-tasks.js index 078b3155a..c8e3c04d4 100644 --- a/test/gruntfile/load-npm-tasks.js +++ b/test/gruntfile/load-npm-tasks.js @@ -4,8 +4,8 @@ var Log = require('grunt-legacy-log').Log; var assert = require('assert'); var through = require('through2'); -module.exports = function(grunt) { - grunt.file.setBase('../fixtures/load-npm-tasks'); +function test(grunt, fixture) { + grunt.file.setBase('../fixtures/' + fixture); // Create a custom log to assert output var stdout = []; @@ -39,4 +39,11 @@ module.exports = function(grunt) { throw err; } }); +} + +module.exports = function(grunt) { + // NPM task package is inside $CWD/node_modules + test(grunt, 'load-npm-tasks'); + // NPM task package hoisted to $CWD/../node_modules + test(grunt, 'load-npm-tasks/test-package'); };