“Composer require local package”: Working with symlinked Composer packages in PHP

When developing a PHP library/package, you most likely also have a project on disk that consumes said library. Take a WordPress plugin for example: to test it, you need a WordPress installation — both are linked but separate projects. To speed up development you can tell Composer to use the local version of the package, instead of having to copy files from folder to folder as you develop.

~

I’m assuming a folder structure like this:

bramus in ~/repos
$ tree -L 1
.
├── my-library
└── my-project

If my-project were to use the published version of my-library, you would run the following command:

bramus in ~/repos/my-project
$ composer require author/my-library

The ~/repos/my-project/composer.json would then look as follows:

{
    // …
    "require": {
        "author/my-library": "*"
    }
}

~

During development of my-library you don’t want to be editing the copy of my-library which resides inside the my-project/vendor/… folder. What you want is to edit the local version of ~/repos/my-library directly, and make my-project use that version.

To do this, Composer allows you to configure the package sources using the repositories option of its configuration. To refer to the local copy adjust ~/repos/my-project/composer.json so that it has an entry pointing to ~/repos/my-library/:

{
    // …
    "require": {
        "author/my-library": "*"
    },
    "repositories": [
        {
            "type": "path",
            "url": "../my-library"
        }
    ]
}

With this addition, re-run the composer require command, and it’ll symlink ~/repos/my-library/ into ~/repos/my-project/vendor/author/my-library/:

bramus in ~/repos/my-project
$ composer require author/my-library

- Installing author/my-library (dev)
Symlinked from ../my-library

🐛 Not seeing this output? Try removing your vendor/ folder first, and then re-require your package. You might also need to add :dev-branchname to your requirement, e.g. composer require author/my-library:dev-master

When you now edit code inside ~/repos/my-library/, the ~/repos/my-project will also be up-to-date 🙂

💁‍♂️ You can also use repositories to refer to privately published repositories. No need to fiddle with Satis or the like

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Working with symlinked packages in React Native

For an RN app I’m co-developing we have several repos that work together. One of the repos acts a library for other repos to use. During development, in order to test a few things out, we sometimes need to have the local dev version of the library repo work with one of the other repos (e.g. the local dev version of the library-repo is a dependency of another repo).

For regular JS apps we’d use yarn link to get this working. For React Native however, that approach doesn’t work: the Metro Bundler can’t cope with symlinked dependencies (See facebook/metro issue #1).

The solution we found was to use wml – which uses watchman under the hood – for this:

Wml is an alternative to symlinks that actually copies changed files from source to destination folders.

Wml is a CLI tool that works pretty much like ln -s. You first set up your links by using the wml add command and then run the wml service (wml start) to start listening. That’s it!

Usage is as follows:

# add the link to wml using `wml add <src> <dest>`
wml add ~/my-package ~/main-project/node_modules/my-package

# start watching all links added
wml start

Installation per NPM/Yarn:

yarn global add wml

wml – Tangible symlinks →

Do note that wml is not perfect. Quite regularly we noticed that things just stopped working, and wrong (cached) includes were made, the bundler would complain about two packages providing React Native, etc. In that case the solution was to quit and reset just about everything:

# remove all wml links
wml rm all

# reset watchman
watchman watch-del-all

# clean up node_modules and reinstall dependencies
rm -rf ./node_modules
yarn install

# now re-add your wml links

Also note that wml alters your .watchmanconfig file in the source folder so that it ignores the locale node_modules folder. Don’t forget to reset it once you’ve stopped with wml.

So yeah … it’s a bit of an unstable solution (but it might help you forward).

Reading up on the original Metro issue I noticed that this possible workaround was mentioned in it … haven’t tested it though.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)