|
25 | 25 | "aarch64-darwin" |
26 | 26 | ]; |
27 | 27 |
|
28 | | - forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); |
| 28 | + # nixpkgs.lib.genAttrs already takes a list and a function — no wrapper needed. |
| 29 | + forAllSystems = nixpkgs.lib.genAttrs systems; |
29 | 30 |
|
30 | 31 | lib = rec { |
31 | 32 | inherit versionMap; |
|
61 | 62 | } |
62 | 63 | else |
63 | 64 | throw "Node.js version ${version} not found in versionMap"; |
64 | | - |
65 | | - getNodejs = |
66 | | - { system, version }: |
67 | | - let |
68 | | - pkgs = getNixpkgs { inherit system version; }; |
69 | | - attrName = versionMap.${version}.attr or "nodejs"; |
70 | | - attrPath = nixpkgs.lib.splitString "." attrName; |
71 | | - in |
72 | | - nixpkgs.lib.attrByPath attrPath (throw "Attribute ${attrName} not found") pkgs; |
73 | 65 | }; |
74 | 66 |
|
75 | 67 | packagesForSystem = |
76 | 68 | system: |
77 | 69 | let |
78 | 70 | pkgs = nixpkgs.legacyPackages.${system}; |
79 | 71 |
|
80 | | - basePackages = builtins.mapAttrs ( |
81 | | - version: versionInfo: lib.getNodejs { inherit system version; } |
| 72 | + # Import each pinned nixpkgs exactly once per version. |
| 73 | + # All package sets below (base, yarn, pnpm) read from this map, |
| 74 | + # so getNixpkgs is never called more than once per version per system. |
| 75 | + perVersionPkgs = builtins.mapAttrs ( |
| 76 | + version: _: lib.getNixpkgs { inherit system version; } |
82 | 77 | ) versionMap; |
83 | 78 |
|
| 79 | + basePackages = builtins.mapAttrs ( |
| 80 | + version: versionPkgs: |
| 81 | + let |
| 82 | + attrName = versionMap.${version}.attr or "nodejs"; |
| 83 | + attrPath = nixpkgs.lib.splitString "." attrName; |
| 84 | + in |
| 85 | + nixpkgs.lib.attrByPath attrPath (throw "Attribute ${attrName} not found") versionPkgs |
| 86 | + ) perVersionPkgs; |
| 87 | + |
84 | 88 | # Create aliases like nodejs_20_18 for 20.18 |
85 | 89 | aliases = nixpkgs.lib.mapAttrs' ( |
86 | 90 | version: pkg: |
87 | 91 | nixpkgs.lib.nameValuePair ("nodejs_" + (builtins.replaceStrings [ "." ] [ "_" ] version)) pkg |
88 | 92 | ) basePackages; |
89 | 93 |
|
90 | | - # Create yarn packages bundled with the specific node version |
| 94 | + # Create yarn packages bundled with the specific node version. |
91 | 95 | # Uses the version-pinned nixpkgs to ensure yarn/node compatibility |
92 | 96 | # (e.g. Node 16 gets yarn 1.x, avoiding OpenSSL/API mismatches with newer yarn). |
93 | 97 | # Falls back to latest nixpkgs if yarn is absent from the pinned rev. |
| 98 | + # yarn has consistently accepted a nodejs override argument across all nixpkgs |
| 99 | + # versions in the supported range, so no __functionArgs guard is needed here. |
94 | 100 | yarnPackages = nixpkgs.lib.mapAttrs' ( |
95 | 101 | version: pkg: |
96 | 102 | let |
97 | | - versionPkgs = lib.getNixpkgs { inherit system version; }; |
| 103 | + versionPkgs = perVersionPkgs.${version}; |
98 | 104 | yarnPkgs = if builtins.hasAttr "yarn" versionPkgs then versionPkgs else pkgs; |
99 | 105 | in |
100 | 106 | nixpkgs.lib.nameValuePair ("yarn_" + (builtins.replaceStrings [ "." ] [ "_" ] version)) ( |
|
110 | 116 |
|
111 | 117 | # Create pnpm packages bundled with the specific node version. |
112 | 118 | # Strategy: use the pnpm that ships in the same pinned nixpkgs as the Node |
113 | | - # version |
| 119 | + # version — it is already era-compatible (e.g. pnpm 8 with Node 18.16). |
114 | 120 | # |
115 | 121 | # Older nixpkgs keep pnpm under nodePackages.pnpm; newer ones promote it to |
116 | | - # pkgs.pnpm with a callable-attrset override that accepts a nodejs argument. |
117 | | - # When that override is available we thread |
| 122 | + # pkgs.pnpm with a callable-attrset override that accepts a nodejs argument |
| 123 | + # (detectable via .__functionArgs). When that override is available we thread |
118 | 124 | # our exact Node derivation through it; otherwise we use pnpm as-is. |
119 | 125 | # |
120 | 126 | # Falls back to latest nixpkgs pnpm only when the pinned rev has no pnpm at |
121 | 127 | # all (rare, but guards against evaluation errors). |
122 | 128 | pnpmPackages = nixpkgs.lib.mapAttrs' ( |
123 | 129 | version: pkg: |
124 | 130 | let |
125 | | - versionPkgs = lib.getNixpkgs { inherit system version; }; |
| 131 | + versionPkgs = perVersionPkgs.${version}; |
126 | 132 |
|
| 133 | + # Prefer top-level pkgs.pnpm (pnpm 10+ era); fall back to nodePackages.pnpm |
127 | 134 | pinnedPnpm = |
128 | 135 | if builtins.hasAttr "pnpm" versionPkgs then |
129 | 136 | versionPkgs.pnpm |
130 | 137 | else |
131 | 138 | versionPkgs.nodePackages.pnpm or null; |
132 | 139 |
|
| 140 | + # In newer nixpkgs pnpm.override is a callable attrset whose __functionArgs |
| 141 | + # mirror the original package function — check for nodejs there. |
133 | 142 | pnpmOverride = if builtins.isNull pinnedPnpm then null else pinnedPnpm.override or null; |
134 | 143 | canOverrideNodejs = |
135 | 144 | builtins.isAttrs pnpmOverride |
|
158 | 167 | basePackages // aliases // yarnPackages // pnpmPackages; |
159 | 168 | in |
160 | 169 | { |
| 170 | + # Standard Flake Outputs |
161 | 171 | packages = forAllSystems ( |
162 | 172 | system: |
163 | | - (packagesForSystem system) |
164 | | - // { |
165 | | - # Default to latest LTS (22.22) |
166 | | - default = (packagesForSystem system)."22.22"; |
167 | | - } |
| 173 | + let |
| 174 | + allPkgs = packagesForSystem system; |
| 175 | + in |
| 176 | + allPkgs // { default = allPkgs."22.22"; } |
168 | 177 | ); |
169 | 178 |
|
| 179 | + # Overlay allows users to use these packages in their own nixpkgs instance |
170 | 180 | overlays.default = |
171 | 181 | final: prev: |
172 | 182 | let |
173 | 183 | pkgsForSystem = packagesForSystem final.system; |
174 | 184 | in |
175 | 185 | pkgsForSystem; |
176 | 186 |
|
| 187 | + # Formatter for the project |
177 | 188 | formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixpkgs-fmt); |
178 | 189 |
|
179 | 190 | # Library functions for integration (compatible with asdf2nix API) |
|
0 commit comments