Usando nodejs en OpenBSD

2022/05/25

TL;DR:

$ doas pkg_add node
$ cat > ~/.npmrc <<'END'
prefix = ${HOME}/.npm-packages
END
# agregar la linea `PATH="$PATH:$HOME/.npm-packages/bin"` en el archivo `~/.profile`

Instalando nodejs en OpenBSD

Por suerte nodejs se encuentra entre los ports de OpenBSD, por lo cual se instala haciendo:

$ doas pkg_add node

Probamos que funcione:

$ node
Welcome to Node.js v16.14.2.
Type ".help" for more information.
>
$ npm version
{
  npm: '8.5.0',
  node: '16.14.2',
  v8: '9.4.146.24-node.20',
  uv: '1.44.1',
  zlib: '1.2.12',
  brotli: '1.0.9',
  ares: '1.18.1',
  modules: '93',
  nghttp2: '1.47.0',
  napi: '8',
  llhttp: '6.0.4',
  openssl: '1.1.1o',
  cldr: '41.0',
  icu: '71.1',
  tz: '2022a',
  unicode: '14.0'
}

Configurando prefijo global para el usuario

Ahora, a instalar algunos utilitario, por ejemplo browser-sync:

$ npm install -g browser-sync
...
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/browser-sync
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/browser-sync'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/browser-sync'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/lib/node_modules/browser-sync'
npm ERR! }
...

Se comienza a buscar en las preferencias de npm el porqué se intenta instalar bajo /usr/local/lib/node_modules en lugar de bajo $HOME:

$ npm config list -l | grep /usr/local
globalconfig = "/usr/local/etc/npmrc"
prefix = "/usr/local"
shell = "/usr/local/bin/bash"

El valor prefix es sospechoso, ya que en la documentación de npm lo primero que se ve lee es:

Directories

See folders to learn about where npm puts stuff.

In particular, npm has two modes of operation:

local mode: npm installs packages into the current project directory, which defaults to the current working directory. Packages install to ./node_modules, and bins to ./node_modules/.bin.
global mode: npm installs packages into the install prefix at $npm_config_prefix/lib/node_modules and bins to $npm_config_prefix/bin.

Local mode is the default. Use -g or –global on any command to run in global mode instead.

Por esto es que debemos cambiar el valor del prefix para que apunte a nuestro $HOME.

La configuración de npm se almacena usando archivos npmrc de la siguiente forma:

Files

The four relevant files are:

per-project config file (/path/to/my/project/.npmrc)
per-user config file (~/.npmrc)
global config file ($PREFIX/etc/npmrc)
npm builtin config file (/path/to/npm/npmrc)

Por lo que se crea el archivo ~/.npmrc de la siguiente forma:

$ cat > ~/.npmrc <<'END'
prefix = ${HOME}/.npm-packages
END

Nota: si tratamos de crear el archivo con npm obtenemos el siguiente error:

$ npm config -g set prefix '${HOME}/.npmrc'
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/etc
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/etc'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/etc'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/etc'
npm ERR! }

Probando instalación global

Luego volvemos a instalar browser-sync:

$ npm install -g browser-sync

added 169 packages, and audited 170 packages in 29s

6 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

y vemos que esta vez funcionó sin problemas.

Para ejecutar browser-sync primero hay que agregar la ruta donde npm instala los binarios que la obtenemos también de la documentación:

Executables

When in global mode, executables are linked into {prefix}/bin on Unix, or directly into {prefix} on Windows. Ensure that path is in your terminal’s PATH environment to run them.

When in local mode, executables are linked into ./node_modules/.bin so that they can be made available to scripts run through npm. (For example, so that a test runner will be in the path when you run npm test.)

Se edita ~/.profiles para agregar la linea PATH="$PATH:$HOME/.npm-packages/bin", se recarga el profile (no hay problemas si es idempotente) y confirmamos que el comando browser-sync funciona:

$ browser-sync --version
2.27.10

Para probar hacemos:

$ cat > index.html <<'END'
<body>
<h1>Hello world!</h1>
END
$ browser-sync -w

y vemos que se abre una nueva pestaña en el navegador web y recarga el archivo luego de modificado.

¿Porqué ese ejemplo para index.html?

  1. Para recordar que actualmente los navegadores parsean casi cualquier cosa como HTML (y lamentar que no se requiera un parsing más exacto usando XHTML).
  2. Porque en la documentación de browser-sync se indica que tiene que haber por lo menos un tag <body> donde se inyecta un script utilizado por browser-sync para hacer la recarga de la página.