Description
When a dependency has been symlinked into a monorepo application, and this dependency is hoisted to the monorepo root, the dependency's tasks/
folder cannot be located by grunt.loadNpmTasks
due to an incorrect assumption made when determining the root filepath.
The core issue is that the name
parameter that loadNpmTasks
is invoked with is not required to be a valid path part due to symlinks. Consider the example directory layout:
dev/
corp-utils-monorepo/
applications/
neato-task-plugins/
tasks/
cool-task.js
package.json
// other stuff from the company
my-team-monorepo/
packages/
team-app-1/
Gruntfile.js
package.json
// other monorepo projects that also use the neato-task-plugins package
As a Good Company, scopes are used to guard internal packages from dependency squatting, and thus neato-task-plugins
is published internally with the @mycorp
scope. Its tasks are loaded from the team-app-1
package Gruntfile with grunt.loadNpmTasks('@mycorp/neato-task-plugins');
All is well, until one tries to develop a new corp-wide task and symlinks the task package by running e.g. yarn link '@mycorp/neato-task-plugins'
from the my-team-monorepo
directory.
Now, running the previously working grunt tasks fail with an error logged from here:
Lines 412 to 417 in ee722d1
e.g.
>> Local Npm module "@mycorp/neato-task-plugins" not found. Is it installed?
And caused by the setting of root
here:
Line 384 in ee722d1
Where the variables have values like:
pkgfile === '/Users/me/dev/corp-utils-monorepo/applications/neato-task-plugins/package.json';
normailzedName === '@mycorp/neato-task-plugins';
// root === '/Users/me/dev/corp-utils-monorepo/appli'
However, we even after we fix the calculation of root
, e.g. by doing something like slicing off 2 (or 3 for scoped packages) path elements, the code still fails to compute tasksdir
correctly, looking for
/Users/me/dev/corp-utils-monorepo/applications/@mycorp/neato-task-plugins/tasks
instead of
/Users/me/dev/corp-utils-monorepo/applications/neato-task-plugins/tasks
.
I think the best solution is to use relative paths from the resolved pkgfile
, rather than attempt to use path.join
with varied arguments of root
, name
, or node_modules
:
var tasksdir = path.join(path.dirname(pkgfile), 'tasks');
I'm not sure what the behavior should be for "collection plugins" that are symlinked, as I'm not familiar with those.