8000 GitHub - fzakaria/jinx: Nix, transpiled to TypeScript · GitHub
[go: up one dir, main page]

Skip to content
fzakariaPublic

About

Nix, transpiled to TypeScript

Resources

Stars

35 stars

Watchers

0 watching

Forks

Latest commit

 

History

82 Commits

Folders and files

Repository files navigation

jinx

Nix, transpiled to TypeScript.

jinx reads Nix and writes TypeScript that evaluates to the same thing. The output is a program you can read, type-check, debug, profile and run in a browser.

$ jinx compile pkgs/by-name/he/hello/package.nix
// Generated by jinx from .../pkgs/by-name/he/hello/package.nix. Do not edit.
import * as nix from "../../../../.jinx/runtime/index.ts";

const here = nix.here(import.meta.url);
const formals$1 = { args: { callPackage: false, fetchurl: false, hello: false, lib: false, nixos: false, stdenv: false, testers: false }, ellipsis: false };

export default nix.lambda((args) => {
  const $ = nix.formals(args, formals$1);
  const callPackage = nix.arg($, "callPackage");
  const fetchurl = nix.arg($, "fetchurl");
  ...
  return nix.app(nix.sel(stdenv, "mkDerivation"), (finalAttrs) => ({
    pname: "hello",
    version: "2.12.2",
    src: nix.thunk(() => nix.app(fetchurl, {
      url: nix.thunk(() => nix.str`mirror://gnu/hello/hello-${nix.sel(finalAttrs, "version")}.tar.gz`),
      hash: "sha256-...",
    })),
    ...
  }));
}, formals$1);
$ jinx eval hello.nix
{ drv = "/nix/store/j8q5j0x495vfrxgha5m6q40yjdi59334-hello-2.12.2.drv"; out = "/nix/store/18bbdvag5v2f3d4y37pdbkzvh7s71cw4-hello-2.12.2"; }
$ nix-instantiate --eval --strict hello.nix
{ drv = "/nix/store/j8q5j0x495vfrxgha5m6q40yjdi59334-hello-2.12.2.drv"; out = "/nix/store/18bbdvag5v2f3d4y37pdbkzvh7s71cw4-hello-2.12.2"; }

Documentation: Usage · Why · Design · Semantics · Verification · Performance · Constraints · Comparisons · The compiled nixpkgs

Status

ci workflow

  • nixpkgs 6510408d, every attribute of ci/eval/outpaths.nix on x86_64-linux, aarch64-linux and aarch64-darwin, jinx against nix-eval-jobs: 231,777 attributes, 229,063 identical drvPaths, 2,714 failing on both sides, 0 differing, 0 failing on one side only. 14 minutes on 32 cores; nix-eval-jobs takes 26.
  • nix-conformance: 1,108 of 1,305 cases, every .drv and NAR case among them (verification).
  • The compiled nixpkgs is published at fzakaria/jinx-nixpkgs, one commit per tracked revision.
  • jinx evaluates nixpkgs in a browser tab from that tree, fetching the modules it needs: hello in 3 s over the network, Nix's drvPaths.
  • Per attribute, jinx is 2.5x to 5.1x slower than Nix (performance).
  • All 41,313 .nix files in nixpkgs parse in 8 s and compile in 28 s.

Why

Nix evaluates nixpkgs with an interpreter, and an interpreter gives you a value. A compiler gives you a program, and once nixpkgs is a TypeScript program the tools built for TypeScript work on it: step through stdenv.mkDerivation in a debugger, profile an evaluation to see which package the time goes to, bundle the modules one configuration reaches and nothing else, evaluate a package set in a browser with no server behind it. A derivation computed by jinx has the same drvPath as one computed by Nix, so everything built on top of it stays compatible with the Nix store and with nix-store --realise.

How it works

Each .nix file becomes one .ts module of the same name in a tree that mirrors the source. A lambda is an arrow function, a let is a block of consts, an attribute set is an object literal, import is require. Nix is lazy, so the emitted code wraps an expression in nix.thunk(() => ...) wherever Nix would allocate a thunk and calls nix.force wherever Nix would force one, by the same rules as Nix's evaluator, so sharing and evaluation order match.

The runtime (src/runtime/) is the value model, the builtins, string contexts, and the derivation code that turns an attribute set into an ATerm, hashes it, and computes output paths. Evaluation writes nothing to /nix/store; jinx instantiate writes the .drv closure afterwards for nix-store --realise.

Writing derivations in TypeScript

The compiled form is faithful to Nix down to every thunk, which makes it exact and makes it tedious to write by hand. src/lib is the hand-written form: a view over a Nix value where attribute reads force, calls apply, and plain objects, arrays, strings and numbers pass as arguments. It interoperates with nixpkgs, so a package written in TypeScript can call stdenv.mkDerivation and depend on anything in pkgs. examples/hello.ts:

import { loadNixpkgs } from "jinx";

const pkgs = loadNixpkgs("/path/to/nixpkgs", { system: "x86_64-linux" });

const hello = pkgs.stdenv.mkDerivation({
  pname: "hello",
  version: "2.12.2",
  src: pkgs.fetchurl({
    url: "mirror://gnu/hello/hello-2.12.2.tar.gz",
    hash: "sha256-WpqZbcKSzCTc9BHO6H6S9qrluNE72caBm0x6nc4IGKs=",
  }),
  meta: { license: pkgs.lib.licenses.gpl3Plus },
});

console.log(hello.drvPath);

The same runtime evaluates nixpkgs underneath, so the drvPath is the one Nix would compute, and jinx instantiate writes it to the store.

Running

$ nix develop                       # node, tsc, nix, nix-eval-jobs
$ node src/cli/main.ts eval file.nix
$ node src/cli/main.ts compile <nixpkgs> --out work/nixpkgs
$ npm test                          # unit tests; JINX_NIX_DIFF=1 adds the nix comparisons
$ npm run corpus                    # tests/corpus against nix-instantiate
$ nix flake check                   # all of the above, offline, plus nix-conformance
$ nix run .#serve                   # the site, with nixpkgs in the browser served from the store
$ nix fmt                           # before committing

No npm dependencies. Node 24 runs the TypeScript directly and tsc only checks; @types/node is pinned by hash in nix/types-node.nix.

Credits

  • psyclyx/fix for the whole-nixpkgs drvPath differential and the clearest written account of derivation hashing outside the Nix source.
  • snix (formerly tvix) for a decade of findings on what is hard about reimplementing Nix.
  • numtide/nix-conformance for turning Nix's test suite into something any implementation can be held to.

License

MIT, see LICENSE. The transpiled nixpkgs trees are derived from nixpkgs, which is MIT.

About

Nix, transpiled to TypeScript

Resources

Stars

35 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

0