From 8f01ce2eb48f7d168ee7c18562fdc7eec1d513b2 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sat, 31 Jan 2026 18:06:53 +0800 Subject: [PATCH] fix: handle __functor in forceFunction --- nix-js/runtime-ts/src/builtins/derivation.ts | 33 +++++++++++++++----- nix-js/runtime-ts/src/type-assert.ts | 21 +++++++++---- nix-js/src/codegen.rs | 13 ++++---- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/nix-js/runtime-ts/src/builtins/derivation.ts b/nix-js/runtime-ts/src/builtins/derivation.ts index ed56779..81d2052 100644 --- a/nix-js/runtime-ts/src/builtins/derivation.ts +++ b/nix-js/runtime-ts/src/builtins/derivation.ts @@ -204,14 +204,32 @@ interface FixedOutputInfo { hashMode: string; } -const extractFixedOutputInfo = (attrs: NixAttrs): FixedOutputInfo | null => { +const extractFixedOutputInfo = (attrs: NixAttrs, ignoreNulls: boolean): FixedOutputInfo | null => { if (!("outputHash" in attrs)) { return null; } + const hashValue = force(attrs.outputHash); + if (ignoreNulls && hashValue === null) { + return null; + } const hash = forceStringValue(attrs.outputHash); - const hashAlgo = "outputHashAlgo" in attrs ? forceStringValue(attrs.outputHashAlgo) : "sha256"; - const hashMode = "outputHashMode" in attrs ? forceStringValue(attrs.outputHashMode) : "flat"; + + let hashAlgo = "sha256"; + if ("outputHashAlgo" in attrs) { + const algoValue = force(attrs.outputHashAlgo); + if (!(ignoreNulls && algoValue === null)) { + hashAlgo = forceStringValue(attrs.outputHashAlgo); + } + } + + let hashMode = "flat"; + if ("outputHashMode" in attrs) { + const modeValue = force(attrs.outputHashMode); + if (!(ignoreNulls && modeValue === null)) { + hashMode = forceStringValue(attrs.outputHashMode); + } + } if (hashMode !== "flat" && hashMode !== "recursive") { throw new Error(`derivation: invalid outputHashMode '${hashMode}' (must be 'flat' or 'recursive')`); @@ -234,14 +252,13 @@ export const derivationStrict = (args: NixValue): NixAttrs => { const builder = validateBuilder(attrs, collectedContext); const platform = validateSystem(attrs); - const outputs = extractOutputs(attrs); - const fixedOutputInfo = extractFixedOutputInfo(attrs); - validateFixedOutputConstraints(fixedOutputInfo, outputs); - const structuredAttrs = "__structuredAttrs" in attrs ? force(attrs.__structuredAttrs) === true : false; - const ignoreNulls = "__ignoreNulls" in attrs ? force(attrs.__ignoreNulls) === true : false; + const outputs = extractOutputs(attrs); + const fixedOutputInfo = extractFixedOutputInfo(attrs, ignoreNulls); + validateFixedOutputConstraints(fixedOutputInfo, outputs); + if ("__contentAddressed" in attrs && force(attrs.__contentAddressed) === true) { throw new Error("ca derivations are not supported"); } diff --git a/nix-js/runtime-ts/src/type-assert.ts b/nix-js/runtime-ts/src/type-assert.ts index e3fe2e9..be700e2 100644 --- a/nix-js/runtime-ts/src/type-assert.ts +++ b/nix-js/runtime-ts/src/type-assert.ts @@ -16,7 +16,6 @@ import type { } from "./types"; import { isStringWithContext, isNixPath } from "./types"; import { force } from "./thunk"; -import { getStringValue } from "./string-context"; import { isAttrs, isFunction, typeOf } from "./builtins/type-check"; /** @@ -32,15 +31,25 @@ export const forceList = (value: NixValue): NixList => { }; /** - * Force a value and assert it's a function - * @throws TypeError if value is not a function after forcing + * Force a value and assert it's a function or functor + * @throws TypeError if value is not a function or functor after forcing */ export const forceFunction = (value: NixValue): NixFunction => { const forced = force(value); - if (!isFunction(forced)) { - throw new TypeError(`Expected function, got ${typeOf(forced)}`); + if (isFunction(forced)) { + return forced; } - return forced; + if ( + typeof forced === "object" && + !Array.isArray(forced) && + forced !== null && + "__functor" in forced + ) { + const functorSet = forced as NixAttrs; + const functor = forceFunction(functorSet.__functor); + return (arg: NixValue) => forceFunction(functor(functorSet))(arg); + } + throw new TypeError(`Expected function, got ${typeOf(forced)}`); }; /** diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index ee3a375..7b52c56 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -473,15 +473,14 @@ impl Compile for [(ExprId, ExprId)] { let inner_ir = ctx.get_ir(inner); let inner_span = inner_ir.span(); - code!(buf, "let expr{}=Nix.createThunk(()=>(", slot.0); - inner_ir.compile(ctx, buf); code!( - buf, - "),\"expr{} {}:{}:{}\");", - slot.0, - ctx.get_current_source().get_name(), - usize::from(inner_span.start()), + buf, ctx; + "let expr" slot.0 "=Nix.createThunk(()=>(" inner_ir ")," + "\"expr" slot.0 " " + ctx.get_current_source().get_name() ":" + usize::from(inner_span.start()) ":" usize::from(inner_span.end()) + "\");" ); } }