feat: toJSON

This commit is contained in:
2026-01-11 16:43:02 +08:00
parent 4c505edef5
commit 3b6804dde6
4 changed files with 68 additions and 54 deletions

View File

@@ -7,6 +7,7 @@ import { isStringWithContext } from "../types";
import { force } from "../thunk"; import { force } from "../thunk";
import { type NixStringContext, mkStringWithContext, addBuiltContext } from "../string-context"; import { type NixStringContext, mkStringWithContext, addBuiltContext } from "../string-context";
import { forceFunction } from "../type-assert"; import { forceFunction } from "../type-assert";
import { nixValueToJson } from "../conversion";
const convertJsonToNix = (json: unknown): NixValue => { const convertJsonToNix = (json: unknown): NixValue => {
if (json === null) { if (json === null) {
@@ -55,8 +56,13 @@ export const fromTOML = (e: NixValue): never => {
throw new Error("Not implemented: fromTOML"); throw new Error("Not implemented: fromTOML");
}; };
export const toJSON = (e: NixValue): never => { export const toJSON = (e: NixValue): NixString => {
throw new Error("Not implemented: toJSON"); const context: Set<string> = 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 => { export const toXML = (e: NixValue): never => {

View File

@@ -9,6 +9,7 @@ import {
isStringWithContext, isStringWithContext,
HAS_CONTEXT, HAS_CONTEXT,
} from "../string-context"; } from "../string-context";
import { nixValueToJson } from "../conversion";
const forceAttrs = (value: NixValue): NixAttrs => { const forceAttrs = (value: NixValue): NixAttrs => {
const forced = force(value); 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)); return argsList.map((a) => coerceToString(a, StringCoercionMode.ToString, false, outContext));
}; };
const nixValueToJson = (value: NixValue, seen = new Set<object>(), 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<string, any> = {};
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 = ( const extractEnv = (
attrs: NixAttrs, attrs: NixAttrs,
structuredAttrs: boolean, structuredAttrs: boolean,

View File

@@ -249,7 +249,7 @@ export const builtins: any = {
builtins: createThunk(() => builtins), builtins: createThunk(() => builtins),
currentSystem: createThunk(() => { currentSystem: createThunk(() => {
return "x86_64-linux" return "x86_64-linux";
}), }),
currentTime: createThunk(() => Date.now()), currentTime: createThunk(() => Date.now()),

View File

@@ -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<object>(),
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<string, any> = {};
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`);
};