From 3b6804dde6825b879f9315612b8e3e25dc3185be Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sun, 11 Jan 2026 16:43:02 +0800 Subject: [PATCH] feat: toJSON --- nix-js/runtime-ts/src/builtins/conversion.ts | 10 +++- nix-js/runtime-ts/src/builtins/derivation.ts | 52 +----------------- nix-js/runtime-ts/src/builtins/index.ts | 2 +- nix-js/runtime-ts/src/conversion.ts | 58 ++++++++++++++++++++ 4 files changed, 68 insertions(+), 54 deletions(-) create mode 100644 nix-js/runtime-ts/src/conversion.ts diff --git a/nix-js/runtime-ts/src/builtins/conversion.ts b/nix-js/runtime-ts/src/builtins/conversion.ts index 19ffb1a..e030e69 100644 --- a/nix-js/runtime-ts/src/builtins/conversion.ts +++ b/nix-js/runtime-ts/src/builtins/conversion.ts @@ -7,6 +7,7 @@ import { isStringWithContext } from "../types"; import { force } from "../thunk"; import { type NixStringContext, mkStringWithContext, addBuiltContext } from "../string-context"; import { forceFunction } from "../type-assert"; +import { nixValueToJson } from "../conversion"; const convertJsonToNix = (json: unknown): NixValue => { if (json === null) { @@ -55,8 +56,13 @@ export const fromTOML = (e: NixValue): never => { throw new Error("Not implemented: fromTOML"); }; -export const toJSON = (e: NixValue): never => { - throw new Error("Not implemented: toJSON"); +export const toJSON = (e: NixValue): NixString => { + const context: Set = new Set(); + const string = JSON.stringify(nixValueToJson(e, new Set(), context)); + if (context.size === 0) { + return string; + } + return mkStringWithContext(string, context); }; export const toXML = (e: NixValue): never => { diff --git a/nix-js/runtime-ts/src/builtins/derivation.ts b/nix-js/runtime-ts/src/builtins/derivation.ts index 3937f8e..7cf43a3 100644 --- a/nix-js/runtime-ts/src/builtins/derivation.ts +++ b/nix-js/runtime-ts/src/builtins/derivation.ts @@ -9,6 +9,7 @@ import { isStringWithContext, HAS_CONTEXT, } from "../string-context"; +import { nixValueToJson } from "../conversion"; const forceAttrs = (value: NixValue): NixAttrs => { const forced = force(value); @@ -80,57 +81,6 @@ const extractArgs = (attrs: NixAttrs, outContext: NixStringContext): string[] => return argsList.map((a) => coerceToString(a, StringCoercionMode.ToString, false, outContext)); }; -const nixValueToJson = (value: NixValue, seen = new Set(), outContext?: NixStringContext): any => { - const v = force(value); - - if (v === null) return null; - if (typeof v === "boolean") return v; - if (typeof v === "string") return v; - if (typeof v === "number") return v; - - if (typeof v === "object" && HAS_CONTEXT in v && "context" in v) { - if (outContext) { - for (const elem of v.context) { - outContext.add(elem); - } - } - return v.value; - } - - if (typeof v === "bigint") { - const num = Number(v); - if (v > Number.MAX_SAFE_INTEGER || v < Number.MIN_SAFE_INTEGER) { - console.warn(`derivation: integer ${v} exceeds safe range, precision may be lost in __structuredAttrs`); - } - return num; - } - - if (typeof v === "object" && v !== null) { - if (seen.has(v)) { - throw new Error("derivation: circular reference detected in __structuredAttrs"); - } - seen.add(v); - } - - if (Array.isArray(v)) { - return v.map((item) => nixValueToJson(item, seen, outContext)); - } - - if (typeof v === "object") { - const result: Record = {}; - for (const [key, val] of Object.entries(v)) { - result[key] = nixValueToJson(val, seen, outContext); - } - return result; - } - - if (typeof v === "function") { - throw new Error("derivation: cannot serialize function in __structuredAttrs"); - } - - throw new Error(`derivation: cannot serialize ${typeof v} to JSON`); -}; - const extractEnv = ( attrs: NixAttrs, structuredAttrs: boolean, diff --git a/nix-js/runtime-ts/src/builtins/index.ts b/nix-js/runtime-ts/src/builtins/index.ts index 786808d..7234b8d 100644 --- a/nix-js/runtime-ts/src/builtins/index.ts +++ b/nix-js/runtime-ts/src/builtins/index.ts @@ -249,7 +249,7 @@ export const builtins: any = { builtins: createThunk(() => builtins), currentSystem: createThunk(() => { - return "x86_64-linux" + return "x86_64-linux"; }), currentTime: createThunk(() => Date.now()), diff --git a/nix-js/runtime-ts/src/conversion.ts b/nix-js/runtime-ts/src/conversion.ts new file mode 100644 index 0000000..51b8aca --- /dev/null +++ b/nix-js/runtime-ts/src/conversion.ts @@ -0,0 +1,58 @@ +import { HAS_CONTEXT, NixStringContext } from "./string-context"; +import { force } from "./thunk"; +import type { NixValue } from "./types"; + +export const nixValueToJson = ( + value: NixValue, + seen = new Set(), + outContext?: NixStringContext, +): any => { + const v = force(value); + + if (v === null) return null; + if (typeof v === "boolean") return v; + if (typeof v === "string") return v; + if (typeof v === "number") return v; + + if (typeof v === "object" && HAS_CONTEXT in v && "context" in v) { + if (outContext) { + for (const elem of v.context) { + outContext.add(elem); + } + } + return v.value; + } + + if (typeof v === "bigint") { + const num = Number(v); + if (v > Number.MAX_SAFE_INTEGER || v < Number.MIN_SAFE_INTEGER) { + console.warn(`derivation: integer ${v} exceeds safe range, precision may be lost in __structuredAttrs`); + } + return num; + } + + if (typeof v === "object" && v !== null) { + if (seen.has(v)) { + throw new Error("derivation: circular reference detected in __structuredAttrs"); + } + seen.add(v); + } + + if (Array.isArray(v)) { + return v.map((item) => nixValueToJson(item, seen, outContext)); + } + + if (typeof v === "object") { + const result: Record = {}; + for (const [key, val] of Object.entries(v)) { + result[key] = nixValueToJson(val, seen, outContext); + } + return result; + } + + if (typeof v === "function") { + throw new Error("derivation: cannot serialize function in __structuredAttrs"); + } + + throw new Error(`derivation: cannot serialize ${typeof v} to JSON`); +};