Working offline with Yarn
NPM is not very friendly when working with Node.js in an offline environment. Multiple times I found myself npm install
ing on an internet machine just to copy node_modules
to the offline environment and commit it with the entire project.
For a while npmbox worked for me, but issues like trying to reach the internet would randomly appear and cripple my workflow.
I also tried Sinopia, but couldn’t consistently publish new or updated packages to it.
Enter Yarn
With Yarn I’m able to consistently install packages in an offline environment. Their original blog post is helpful, but I met some edge case it doesn’t cover.
This is my process for using Yarn in an offline environment.
Configuring yarn-offline-mirror
On the internet machine:
yarn config set yarn-offline-mirror ~/yarn-offline-mirror/
On the offline machine:
yarn config set yarn-offline-mirror ~/yarn-offline-mirror/
Note: On the offline machine ~/yarn-offline-mirror/
can also be a shared folder or a git repository.
Creating a new project
On the internet machine:
mkdir new-project/
cd new-project/
yarn add dep1@x.y.z [dep2...]
Then copy new-project/yarn.lock
, new-project/package.json
and ~/yarn-offline-mirror/
to the offline machine.
(rm -rf new-project/
is ok now.)
On the offline machine:
mkdir new-project/
cp /path/to/imported/{yarn.lock,package.json} new-project/
cp -n /path/to/imported/yarn-offline-mirror/* ~/yarn-offline-mirror/
cd new-project/
yarn --offline
Adding packages to an existing project
On the internet machine:
mkdir new-packages/
cd new-packages/
yarn add dep1@x.y.z [dep2...]
Then copy new-packages/yarn.lock
, new-packages/package.json
and ~/yarn-offline-mirror/
to the offline machine.
On the offline machine:
-
Append the imported
yarn.lock
to the existingyarn.lock
. I found that without this step Yarn sometimes fails to find the new packages in the offline cache:cat /path/to/imported/yarn.lock >> existing-project/yarn.lock
-
Update
package.json
with the new dependencies. This means merging bothdependencies
fields together. An ugly one-liner I tend to use:cat existing-project/package.json <(cat existing-project/package.json /path/to/imported/package.json | jq '.dependencies' | jq -s 'add | {dependencies: .}') | jq -s add | sponge existing-project/package.json
-
Update
yarn-offline-mirror
:cp -n /path/to/imported/yarn-offline-mirror/* ~/yarn-offline-mirror/
-
Install the new packages. This step also fixes
existing-project/yarn.lock
after we messed with it in step 1.cd existing-project/ yarn --offline
I found the if I skip steps 1 and 2, and in step 4 I do yarn add --offline <dep1> [<dep2>...]
then Yarn might not find the new packages in the cache and fail. This bug still exists in version 1.5.1. I believe it is related to these GitHub issues: [1][2][3][4][5][6].
Installing packages globally
Yarn discourages using global packages, so it’s hard by design to install them.
-
Find out where is the global installation location [7]:
yarn global bin
(Or set it with
yarn config set prefix <file_path>
) -
Add it to your path. E.g.:
echo 'export PATH=$PATH:'"$(yarn global bin)" >> ~/.bashrc source ~/.bashrc # reload
-
Similar to Creating a new project, but with a few subtle differences:
On the internet machine:
mkdir new-cli/ cd new-cli/ yarn add cli1@x.y.z [cli2...]
Then copy
new-cli/yarn.lock
and~/yarn-offline-mirror/
to the offline machine.(
rm -rf new-cli/
is ok now.)On the offline machine:
cp /path/to/imported/yarn.lock . cp -n /path/to/imported/yarn-offline-mirror/* ~/yarn-offline-mirror/ yarn global add --offline cli1@x.y.z [cli2...] rm -f ./yarn.lock
Note: In this context we don’t care about
package.json
. We only need to make sure that Yarn can findyarn.lock
in the current directory and that~/yarn-offline-mirror/
has the needed dependencies.
This is not a silver bullet
There are probably more edge cases I still haven’t met. I wish the docs around yarn.lock
were better, Because playing with this file solved most of my problems.
For example when adding packages to an existing project, I still don’t understand why appending the new lock file to the existing one solves the resolution problem. According to the docs this step is unnecessary. And sometimes it is, but some other times it isn’t.
Overall I’m very satisfied with Yarn and I have a feeling it is only going to get better.