commit 1a55a11500188e617e50b686081d5743c1c8d3ef Author: EatThePooh Date: Tue Aug 5 20:59:34 2025 +0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b8c2ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.emacs* +.deno_cache \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ab7c54 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Deno with packages + +This flake provides utilities for managing Deno dependencies with Nix, +- reading multiple lock files +- creating a derivation per Deno dependency +- combining those into a single derivation +- installing shared cache into `DENO_DIR` + +Currently only NPM dependencies are recognized. + +## Usage + +See `flake.nix` diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..ea94b66 --- /dev/null +++ b/deno.lock @@ -0,0 +1,32 @@ +{ + "version": "5", + "specifiers": { + "npm:@types/node@*": "22.13.4", + "npm:hjson@^3.2.2": "3.2.2", + "npm:jq-web@~0.6.2": "0.6.2" + }, + "npm": { + "@types/node@22.13.4": { + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "dependencies": [ + "undici-types" + ] + }, + "hjson@3.2.2": { + "integrity": "sha512-MkUeB0cTIlppeSsndgESkfFD21T2nXPRaBStLtf3cAYA2bVEFdXlodZB0TukwZiobPD1Ksax5DK4RTZeaXCI3Q==", + "bin": true + }, + "jq-web@0.6.2": { + "integrity": "sha512-+7XvjBYwTx4vP5PYkf6Q6orubO/v+UgMU6By1GritrmShr9QpT3UKa4ANzXWQfhdqtBnQYXsm7ZNbdIHT6tYpQ==" + }, + "undici-types@6.20.0": { + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + } + }, + "workspace": { + "dependencies": [ + "npm:hjson@^3.2.2", + "npm:jq-web@~0.6.2" + ] + } +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..720aa25 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1754214453, + "narHash": "sha256-Q/I2xJn/j1wpkGhWkQnm20nShYnG7TI99foDBpXm1SY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5b09dc45f24cf32316283e62aec81ffee3c3e376", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..84123fa --- /dev/null +++ b/flake.nix @@ -0,0 +1,59 @@ +{ + description = "Builds a shared cache for Deno packages based on lock files"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in rec + { + lib = { + # Extract NPM dependencies from a single Deno lock file + denoLockfileToNpmDeps = import ./lib/lockfile-to-npm-deps.nix { self = lib; }; + + # Create a shared Deno cache from multiple lock files + denoSharedCache = import ./lib/shared-cache.nix { self = lib; }; + + # Install shared cache into DENO_DIR + installDenoCache = import ./lib/install-cache.nix; + + denoNpmRegistryHostname = "registry.npmjs.org"; + }; + + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + deno + ]; + + shellHook = + let + # Example + sharedCache = self.lib.${system}.denoSharedCache { + inherit pkgs; + lockfiles = [ ./deno.lock ]; # Add your lock files here + }; + installScript = self.lib.${system}.installDenoCache { + inherit pkgs; + cache = sharedCache; + }; + in + '' + export DENO_DIR="$PWD/.deno_cache" + + if [ -d "${sharedCache}" ]; then + echo "Installing Deno shared cache..." + ${installScript}/bin/install-deno-cache + fi + + echo "Deno development environment ready!" + echo "DENO_DIR is set to: $DENO_DIR" + ''; + }; + } + ); +} diff --git a/lib/install-cache.nix b/lib/install-cache.nix new file mode 100644 index 0000000..fc28906 --- /dev/null +++ b/lib/install-cache.nix @@ -0,0 +1,34 @@ +{ pkgs, cache }: + +pkgs.writeShellScriptBin "install-deno-cache" '' + set -euo pipefail + + if [ -z "''${DENO_DIR:-}" ]; then + echo "Error: DENO_DIR environment variable must be set" >&2 + exit 1 + fi + + echo "Installing Deno cache to: $DENO_DIR" + + # Remove existing cache if it exists + if [ -d "$DENO_DIR" ]; then + echo "Removing existing cache..." + rm -rf "$DENO_DIR" + fi + + # Copy the shared cache, dereferencing symlinks + if [ -d "${cache}" ]; then + echo "Copying shared cache..." + mkdir -p "$(dirname "$DENO_DIR")" + cp -rL "${cache}" "$DENO_DIR" + chmod -R u+w "$DENO_DIR" + echo "Cache installed successfully!" + else + echo "No shared cache found at ${cache}" + mkdir -p "$DENO_DIR" + fi + + if [ -d "$DENO_DIR/npm" ]; then + echo "NPM packages in cache: $(find "$DENO_DIR/npm" -name "package.json" | wc -l)" + fi +'' diff --git a/lib/lockfile-to-npm-deps.nix b/lib/lockfile-to-npm-deps.nix new file mode 100644 index 0000000..3c6ddce --- /dev/null +++ b/lib/lockfile-to-npm-deps.nix @@ -0,0 +1,56 @@ +{ self }: +{ pkgs, lockfile }: +let + registry = self.denoNpmRegistryHostname; + + # Extract npm dependencies from a single lockfile + extractNpmDeps = lockfile: + let + lockJSON = builtins.fromJSON (builtins.readFile lockfile); + in + lockJSON.npm or {}; + + npmDeps = extractNpmDeps lockfile; + + # Create tarball derivation for a package + mkTarballDrv = { name, version, integrity }: + let + tarballName = + if builtins.substring 0 1 name == "@" + then builtins.baseNameOf name # "@types/node" → "node" + else name; + in + pkgs.fetchurl { + url = "https://${registry}/${name}/-/${tarballName}-${version}.tgz"; + hash = integrity; + }; + + # Create derivation for a single npm package + mkNpmDep = key: value: + let + match = builtins.match "(.+)@(.+)" key; + name = builtins.elemAt match 0; + version = builtins.elemAt match 1; + in + pkgs.stdenv.mkDerivation { + pname = "deno-npm-${builtins.replaceStrings ["@" "/"] ["" "-"] name}"; + version = version; + src = mkTarballDrv { + inherit name version; + inherit (value) integrity; + }; + + installPhase = '' + runHook preInstall + + export OUT_PATH="$out/npm/${registry}/${name}/${version}" + + mkdir -p $OUT_PATH + tar -xzf $src -C $OUT_PATH --strip-components=1 + + runHook postInstall + ''; + }; + +in +pkgs.lib.mapAttrsToList mkNpmDep npmDeps diff --git a/lib/shared-cache.nix b/lib/shared-cache.nix new file mode 100644 index 0000000..afe2ae6 --- /dev/null +++ b/lib/shared-cache.nix @@ -0,0 +1,13 @@ +{ self }: +{ pkgs, lockfiles }: +let + allNpmDeps = builtins.concatMap + (lockfile: + self.denoLockfileToNpmDeps { inherit pkgs lockfile; }) + lockfiles; + +in +pkgs.symlinkJoin { + name = "deno-shared-cache"; + paths = allNpmDeps; +}