Description
This is a feature request to make "yarn link" or a new command be more useful than the current behavior, which was modeled after "npm link". Before focusing on the actual linking mechanism I want to describe the desired behavior of "yarn link":
Desired behavior
The "yarn link" workflow should mimic publishing a package (ex: dep
) to npm and then installing it in a dependent (ex: app
), and keep this constraint while you're making changes to the first package. Concretely, "yarn link" should make it so that when you save a change to dep
, the resulting state is as if you:
- Ran "npm publish" in
dep
(assume that it can clobber an existing version, and that you're publishing to a local registry on just your computer). - Ran "yarn add dep" in
app
.
Why this behavior is great
This solves several problems that "yarn link" has today:
Isolating node_modules
correctly
You can install dep
in two different apps without sharing the node_modules
of dep
. This is a problem with Electron apps, whose V8 version is different than Node's and uses a different ABI. If you have node-app
and electron-app
that both depend on dep
, the native dependencies of dep
need to be recompiled separately for each app; node-app/n_m/dep/n_m
must not be the same as electron-app/n_m/dep/n_m
.
Working on multiple versions
You can be developing multiple different versions of dep
. Say you have two directories, dep-1
and dep-2
, which have your v1 and v2 branches checked out, respectively. With "yarn link" it's not possible to make both of these directories linkable at the same time.
This is a problem when you are developing & testing dep-1
with old-app
and dep-2
with new-app
. You don't want to be going back and forth between dep-1
and dep-2
running "yarn link" each time you switch which app you're testing.
Faithfully representing the node_modules
hierarchy
Currently "yarn link" symlinks the entire package directory, which brings along its node_modules
subdirectory with it. With dependency deduping and flattening, bringing in dep/node_modules
wholesale usually produces a different node_modules
hierarchy than running yarn install
in app
and installing everything from npm. This isn't a problem most of the time but it does go against Yarn's spirit of consistency and the lockfile.
A practical proposal -- knitting :3
This is a proposal that solves all of the problems above and isn't too hard to implement or understand. I'm going to call it "yarn knit" to distinguish it from "yarn link". Conceptually, we find all the files we'd normally publish to npm, pack them up using symlinks instead of copies of the files, publish the pack to a local registry (just a directory), and then when installing we look up packages in the local registry directory instead of npm.
Running "yarn knit" inside of dep
This is the step that simulates publishing dep
. Running "yarn knit" in dep
finds all the files that "yarn publish" would pack up and upload to npm. Crucially, this excludes node_modules
, and would follow the same algorithm as "yarn publish" such as reading package.json's files
field.
Then it simulates publishing dep
: it creates a directory named dep-X.Y.Z
(where X.Y.Z
is the version of dep
in its package.json) inside of a global directory like ~/.yarn-knit
. A symlink is created for each file or directory that "yarn publish" would normally have packed up. This step shares some conceptual similarities with publishing to a registry, except it uses symlinks and it's local on your computer.
Running "yarn knit dep" inside of app
This behaves like "yarn add dep" except that it looks at the versions of dep
that are in the global ~/.yarn-knit
folder and takes the latest one. (You also could run "yarn link dep@X.Y.Z" if you wanted a more specific version, like "yarn add".)
"yarn knit dep" then runs most of the same installation steps that yarn add dep
would. It creates app/node_modules/dep
and creates symlinks for each of the symlinks under ~/.yarn-knit/dep-X.Y.Z
. Then it installs the dependencies of dep
as usual by fetching them from npm. Finally it runs postinstall scripts.
Issues
One issue with this proposal is that it's not clear what to put in the lockfile after running yarn link dep
since we don't have an npm URL for the dep yet -- it hasn't been published to npm. Another issue is that if you change package.json in dep
, namely changing a dependency or modifying the files
entry, you have to run cd dep; yarn knit; cd app; yarn knit dep
.