Migrating from Bower to NPM/Yarn
As we all know, Bower is on its way out, although Bower is still being maintained, Bower maintainers recommend not using Bower anymore and use the combination Yarn + Webpack. Webpack is not a dependency manager so we won’t analyze it, and there’s plenty of information about it now everywhere in the web (I have a couple of posts pending about it though).
Bower
Some of most important characteristics of Bower are:
- Tight git coupling, meaning
- All modules need to be git repositories and
- Module versions are based on git tags
- Lightweight registry with small JSONs pointing to where the git repository is and some metadata
[image of how bower works]
As a very quick refresher, this is what you need to publish a new module to the Bower registry:
git init
git add .
git commit -m "First commit"
git tag v1.0.0
git push origin master --tags
bower register my-module <git-endpoint>
# Then, somewhere else:
bower install my-module
We will see later some other ways to consume bower modules that will help us to give backward compatibility for people still consuming our NPM package with Bower.
NPM / Yarn
First, we need to clarify that both NPM and Yarn are NPM clients, and having said that, I could rephrase the options to npm-cli
and yarn
.
- Both work with NPM packages and they differ on how they install the packages, how fast/secure/deterministic they are installing them,
- And on the other tools they provide, like a way to see packages licenses, find out which packages are using a particular package, etc.
I will not go in depth about the differences between the 2 because I think people have already explained it several times and with great quality. These are some useful articles if you want to read more about its comparison:
I personally prefer Yarn because I found it more consistent and fast installing packages.
Migrating your Bower module
Step 1: Moving your dependencies from bower.json
to package.json
This step should be easy, if you are using packages with some popularity then they should provide a package.json
with their dependencies defined. In that case you just run
yarn add my-bower-dep
# or
npm install --save my-bower-dep
You can use the alternative ways to reference a package with npm, like:
$ npm install https://github.com/indexzero/forever/tarball/v0.5.6
$ npm install git+ssh://git@github.com:npm/npm.git#v1.0.27
$ npm install git+ssh://git@github.com:npm/npm#semver:^5.0
$ npm install git+https://isaacs@github.com/npm/npm.git
$ npm install git://github.com/npm/npm.git#v1.0.27
$ npm install mygithubuser/myproject
$ npm install github:mygithubuser/myproject
Check more in this documentation link.
Now, a problem may araise if your dependency doesn’t have a package.json
defined in its root’s directory. You can either create a pull request for it in your dependency’s repo or use something like napa.
napa’s configuration is pretty simple, taken from its documentation, one clean opproach is like this:
{
"scripts": {
"install": "napa"
},
"napa": {
"foo": "username/repo#v1.2.3",
"bar": "username/bar#some-branch",
"baz": "username/baz#347259472813400c7a982690acaa516292a8be40",
"qoo": "https://example.com/downloads/release.tar.gz",
"fuz": "git+https://yourcompany.com/repos/project.git",
"goo": "git+ssh://yourcompany.com/repos/project.git"
}
}
With these approaches you should have your dependencies covered and defined in your package.json
.
Step 2: Ignoring the dist/
, build/
or whatever
directory
With bower you were obliged to push your distribution files in the repository because the bower install
was a checkout of the specified version/tag. With NPM, you can generate the distribution files during your publication phase and never push it to git.
npm run build
npm publish
Step 3: bower.json > ignore
-> .npmignore
(or package.json > files
)
With the ignore object in the bower.json
we could tell bower to ignore files when being downloaded from our consumers.
With NPM, we can do the same with the .npmignore
file or via the package.json
’s files object.
I prefer the positive approach where I say what files I want in the package, and not have to maintain the .npmignore
every time I add a new configuration file or whatever. It’s safer too, if by any change you forget to ignore a file with some kind of sensitive information then your are screwed.
Step 4: Publishing your package to NPM (all versions if needed)
The step is super simple, change your package.json
’s version property and just npm publish
or yarn publish
.
Some useful references, npm version, npm publish, yarn version, yarn publish.
If you want to publish packages for every tag you have, then repeat this process going back in your history and publishing the package. You have to understand though that your package.json
can’t be “injected” in your git history. You can branch from the tag and push the package.json
or just add it, publish the package, discard it and forget about it.
Remember that you can run
npm publish --tag <tag>
to not only publish your package but also create a git tag for your repository. So you can keep the consistency with your previous git tags. Anyway in my opinion is a good practice to tag released versions.
Step 5: Other useful information
What about bower’s flat installation?
If you see repeated nested dependencies inside node_modules
, don’t panic, it’s ok, with Yarn there’s a way out:
yarn install --flat --prod
Yarn provides the yarn install --flat
command that provides that funcionality. Just remember to divide correctly your dependencies
from your devDependencies
in your package.json
and yarn will do the work for you, prompting you to chose a version if needed, Bower’ style. So you can be sure that you’ll have only 1 version of your dependencies in the browser and no conflicts between your dependencies.
Provide backward compatibility
Do you have consumers that will still consume you via Bower?
- You can leave your
bower.json
if you want, and decide between being responsible of maintaining it or put a big notice in your readme saying that you’ll leave it as backward compatibility until the next version or something like that. - If your package is already registered in the bower registry, then just by creating tags for your releases you make your module downloadeable by bower. But if you don’t have a
bower.json
then your consumers will not download your dependencies (and they will have to add them in their bower.json). Also your distribution files are no longer versioned, even if they download your repo at a tag, they don’t have what they want! So what do you do?
While you maintain your bower.json versioned, generate the distribution files as always and publish the npm package with the bower.json
.
Your consumers will have to consume you like:
dependencies: {
"awesome-now-npm-only-dep": "https://registry.npmjs.org/ember/-/ember-1.0.3.tgz"
}
I put ember
there just as an example, awesome-now-npm-only-dep
would make the url too long :p
Bower will download that .tgz
in their bower_components
and if a bower.json
is present, continue downloading the transitive dependencies. So the cost for you in this approach is leaving the bower.json in your repo and choose a policy for it: maintain it forever or until the version you choose.
And remember, even after you remove it, they still can download your package using the tag and add your dependencies as theirs (definitely not something I would do but…you never know).
Ok, I think this is enough information for you to have something working :) Bye!