nixpkgs/doc/languages-frameworks/bower.section.md
Jan Tojnar 6ecc641d08
doc: prepare for commonmark
We are still using Pandoc’s Markdown parser, which differs from CommonMark spec slightly.

Notably:
- Line breaks in lists behave differently.
- Admonitions do not support the simpler syntax https://github.com/jgm/commonmark-hs/issues/75
- The auto_identifiers uses a different algorithm – I made the previous ones explicit.
- Languages (classes) of code blocks cannot contain whitespace so we have to use “pycon” alias instead of Python “console” as GitHub’s linguist

While at it, I also fixed the following issues:
- ShellSesssion was used
- Removed some pointless docbook tags.
2021-06-07 06:34:59 +02:00

5.7 KiB

Bower

Bower is a package manager for web site front-end components. Bower packages (comprising of build artefacts and sometimes sources) are stored in git repositories, typically on Github. The package registry is run by the Bower team with package metadata coming from the bower.json file within each package.

The end result of running Bower is a bower_components directory which can be included in the web app's build process.

Bower can be run interactively, by installing nodePackages.bower. More interestingly, the Bower components can be declared in a Nix derivation, with the help of nodePackages.bower2nix.

bower2nix usage

Suppose you have a bower.json with the following contents:

Example bower.json

  "name": "my-web-app",
  "dependencies": {
    "angular": "~1.5.0",
    "bootstrap": "~3.3.6"
  }

Running bower2nix will produce something like the following output:

{ fetchbower, buildEnv }:
buildEnv { name = "bower-env"; ignoreCollisions = true; paths = [
  (fetchbower "angular" "1.5.3" "~1.5.0" "1749xb0firxdra4rzadm4q9x90v6pzkbd7xmcyjk6qfza09ykk9y")
  (fetchbower "bootstrap" "3.3.6" "~3.3.6" "1vvqlpbfcy0k5pncfjaiskj3y6scwifxygfqnw393sjfxiviwmbv")
  (fetchbower "jquery" "2.2.2" "1.9.1 - 2" "10sp5h98sqwk90y4k6hbdviwqzvzwqf47r3r51pakch5ii2y7js1")
];

Using the bower2nix command line arguments, the output can be redirected to a file. A name like bower-packages.nix would be fine.

The resulting derivation is a union of all the downloaded Bower packages (and their dependencies). To use it, they still need to be linked together by Bower, which is where buildBowerComponents is useful.

buildBowerComponents function

The function is implemented in pkgs/development/bower-modules/generic/default.nix.

Example buildBowerComponents

<programlisting language="nix">
bowerComponents = buildBowerComponents {
  name = "my-web-app";
  generated = ./bower-packages.nix; <co xml:id="ex-buildBowerComponents-1" />
  src = myWebApp; <co xml:id="ex-buildBowerComponents-2" />
};
</programlisting>

In "buildBowerComponents" example the following arguments are of special significance to the function:

<calloutlist>
  <callout arearefs="ex-buildBowerComponents-1">
    <para>
      <varname>generated</varname> specifies the file which was created by <command>bower2nix</command>.
    </para>
    </callout>
      <callout arearefs="ex-buildBowerComponents-2">
    <para>
      <varname>src</varname> is your project's sources. It needs to contain a <filename>bower.json</filename> file.
    </para>
  </callout>
</calloutlist>

buildBowerComponents will run Bower to link together the output of bower2nix, resulting in a bower_components directory which can be used.

Here is an example of a web frontend build process using gulp. You might use grunt, or anything else.

Example build script (gulpfile.js)

var gulp = require('gulp');

gulp.task('default', [], function () {
  gulp.start('build');
});

gulp.task('build', [], function () {
  console.log("Just a dummy gulp build");
  gulp
    .src(["./bower_components/**/*"])
    .pipe(gulp.dest("./gulpdist/"));
});

Example Full example — default.nix

<programlisting language="nix">
{ myWebApp ? { outPath = ./.; name = "myWebApp"; }
, pkgs ? import &lt;nixpkgs&gt; {}
}:

pkgs.stdenv.mkDerivation {
  name = "my-web-app-frontend";
  src = myWebApp;

  buildInputs = [ pkgs.nodePackages.gulp ];

  bowerComponents = pkgs.buildBowerComponents { <co xml:id="ex-buildBowerComponentsDefault-1" />
    name = "my-web-app";
    generated = ./bower-packages.nix;
    src = myWebApp;
  };

  buildPhase = ''
    cp --reflink=auto --no-preserve=mode -R $bowerComponents/bower_components . <co xml:id="ex-buildBowerComponentsDefault-2" />
    export HOME=$PWD <co xml:id="ex-buildBowerComponentsDefault-3" />
    ${pkgs.nodePackages.gulp}/bin/gulp build <co xml:id="ex-buildBowerComponentsDefault-4" />
  '';

  installPhase = "mv gulpdist $out";
}
</programlisting>

A few notes about Full example — default.nix:

<calloutlist>
  <callout arearefs="ex-buildBowerComponentsDefault-1">
    <para>
      The result of <varname>buildBowerComponents</varname> is an input to the frontend build.
    </para>
  </callout>
  <callout arearefs="ex-buildBowerComponentsDefault-2">
    <para>
      Whether to symlink or copy the <filename>bower_components</filename> directory depends on the build tool in use. In this case a copy is used to avoid <command>gulp</command> silliness with permissions.
    </para>
  </callout>
  <callout arearefs="ex-buildBowerComponentsDefault-3">
    <para>
      <command>gulp</command> requires <varname>HOME</varname> to refer to a writeable directory.
    </para>
  </callout>
  <callout arearefs="ex-buildBowerComponentsDefault-4">
    <para>
      The actual build command. Other tools could be used.
    </para>
  </callout>
</calloutlist>

Troubleshooting

ENOCACHE errors from buildBowerComponents

This means that Bower was looking for a package version which doesn't exist in the generated bower-packages.nix.

If bower.json has been updated, then run bower2nix again.

It could also be a bug in bower2nix or fetchbower. If possible, try reformulating the version specification in bower.json.