jinx Nix, transpiled to TypeScript

Why

Nix evaluates nixpkgs with an interpreter, and an interpreter gives you a value. jinx gives you a program, and the tools built for TypeScript work on it.

Profile an evaluation

node --cpu-prof on firefox.drvPath, folded through the source maps so every frame is a line of nixpkgs:

The top of a flame graph of evaluating firefox.drvPath, every frame a nixpkgs line

The top of the graph. Click it for the whole SVG, with zoom and a share on hover.

The widest frames are lib.makeOverridable in lib/customisation.nix and the firefox wrapper, and the module system's make-derivation.nix. pandoc under the wrapper is a tenth of the run: that is where hackage-packages.nix gets loaded.

$ node --cpu-prof --cpu-prof-dir=prof src/cli/main.ts eval firefox.nix
$ node tools/flamegraph.ts --nix-only prof/*.cpuprofile > stacks.folded
$ flamegraph.pl stacks.folded > flamegraph.svg

Complete nixpkgs in the editor

tools/gen-nixpkgs-dts.ts writes a declaration file for a package set: every attribute, lib's functions with their documented signatures, and the shape of a derivation. With it, pkgs. completes and a misspelled attribute is an error before anything runs. With --positions, hovering an attribute shows the file and line that defines it, as a link VS Code opens.

pkgs.neo completing to neovim, neofetch and the rest; pkgs.rigrep underlined as an error

Step through a package

Source maps put a breakpoint on a .nix line. Open the compiled tree in VS Code or Chrome DevTools, break in pkgs/by-name/he/hello/package.nix, and step into stdenv.mkDerivation as it builds the attribute set. A direct import ./x.nix names its compiled module in a type, so go-to-definition follows it across the tree.

$ node --inspect-brk --enable-source-maps src/cli/main.ts eval hello.nix

Query nixpkgs in a loop

A view of nixpkgs is an object, so a question about it is a loop:

import { loadNixpkgs } from "jinx";

const pkgs = loadNixpkgs("/path/to/nixpkgs");
const gpl = [];
for (const name of Object.keys(pkgs.lib.licenses)) {
  if (pkgs.lib.licenses[name].spdxId?.startsWith("GPL")) {
    gpl.push(name);
  }
}
console.log(JSON.stringify(gpl));

Test Nix with node:test

A lib function or a package definition is a function in a module, so it has a unit test:

import { test } from "node:test";
import assert from "node:assert/strict";
import { loadNixpkgs } from "jinx";

const pkgs = loadNixpkgs("/path/to/nixpkgs");

test("hello builds the derivation Nix would", () => {
  assert.equal(
    pkgs.hello.drvPath,
    "/nix/store/cnraiw0hbdl6jzdhbkn8dah2hqj7xgij-hello-2.12.3.drv",
  );
});

Run it in a browser

The nixpkgs page evaluates attributes of the compiled nixpkgs in the tab, fetching the modules it needs, and the playground compiles and runs a snippet as you type.

Edit this page on GitHub