From 631a2b5d442570b5fe31c45811b3388dbe52a992 Mon Sep 17 00:00:00 2001 From: "arduano@localhost" <> Date: Mon, 20 May 2024 20:30:31 +1000 Subject: [PATCH] Improved path handling for imports Slightly improved path handling for the import builtin, non-absolute string handling will be fixed in the future. --- nixjs-rt/src/builtins.ts | 27 ++++++++++++++++++++++----- nixjs-rt/src/lib.ts | 29 +---------------------------- nixjs-rt/src/utils.ts | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 nixjs-rt/src/utils.ts diff --git a/nixjs-rt/src/builtins.ts b/nixjs-rt/src/builtins.ts index 7f01095..f17ae8e 100644 --- a/nixjs-rt/src/builtins.ts +++ b/nixjs-rt/src/builtins.ts @@ -1,4 +1,4 @@ -import { err, errType, errTypes } from "./errors"; +import { err, errType, errTypes, highlighted } from "./errors"; import { abortError } from "./errors/abort"; import { otherError } from "./errors/other"; import { typeMismatchError } from "./errors/typeError"; @@ -18,6 +18,7 @@ import { Path, TRUE, } from "./lib"; +import { dirOf, isAbsolutePath, normalizePath } from "./utils"; type BuiltinsRecord = Record NixType>; @@ -354,12 +355,28 @@ export function getBuiltins() { throw builtinBasicTypeMismatchError("import", pathStrict, expected); } - const pathValue = pathStrict.toJs(); + let pathValue = ""; + if (pathStrict instanceof NixString) { + pathValue = normalizePath(pathStrict.toJs()); + } else if (pathStrict instanceof Path) { + pathValue = pathStrict.toJs(); + } + + debugLog(JSON.stringify(pathValue)); + debugLog(JSON.stringify(pathStrict instanceof NixString)); + + // Check if it's an absolute path. Relative paths are not allowed. + // Path data types are always automatically absolute. + if (isAbsolutePath(pathValue)) { + throw otherError( + `string ${highlighted(JSON.stringify(pathValue))} doesn't represent an absolute path. Only absolute paths are allowed for imports.`, + ); + } - const resultingFn: (ctx: EvalCtx) => NixType = importNixModule(pathValue); + const resultingFn = importNixModule(pathValue); - const ctx = new EvalCtx(pathValue); - return resultingFn(ctx); + const newCtx = new EvalCtx(dirOf(pathValue)); + return resultingFn(newCtx); }, intersectAttrs: (arg) => { diff --git a/nixjs-rt/src/lib.ts b/nixjs-rt/src/lib.ts index 6e746cc..29720d3 100644 --- a/nixjs-rt/src/lib.ts +++ b/nixjs-rt/src/lib.ts @@ -20,6 +20,7 @@ import { couldntFindVariableError, } from "./errors/variable"; import { NixAbortError } from "./errors/abort"; +import { isAbsolutePath, joinPaths, normalizePath } from "./utils"; // Error re-exports export { NixError } from "./errors"; @@ -1291,34 +1292,6 @@ export function recursiveStrictAttrset(theAttrset: Attrset): Attrset { return theAttrset; } -function isAbsolutePath(path: string): boolean { - return path.startsWith("/"); -} - -function joinPaths(abs_base: string, path: string): string { - return `${abs_base}/${path}`; -} - -function normalizePath(path: string): string { - let segments = path.split("/"); - let normalizedSegments = new Array(); - for (const segment of segments) { - switch (segment) { - case "": - break; - case ".": - break; - case "..": - normalizedSegments.pop(); - break; - default: - normalizedSegments.push(segment); - break; - } - } - return (isAbsolutePath(path) ? "/" : "") + normalizedSegments.join("/"); -} - /** * If given an attrset entry like `a = value`, then this function returns just the given value. * If the attrset has multiple segments (e.g. `a.b.c = value`), then this function returns diff --git a/nixjs-rt/src/utils.ts b/nixjs-rt/src/utils.ts new file mode 100644 index 0000000..6396920 --- /dev/null +++ b/nixjs-rt/src/utils.ts @@ -0,0 +1,33 @@ +export function isAbsolutePath(path: string): boolean { + return path.startsWith("/"); +} + +export function joinPaths(abs_base: string, path: string): string { + return `${abs_base}/${path}`; +} + +export function normalizePath(path: string): string { + let segments = path.split("/"); + let normalizedSegments: string[] = []; + for (const segment of segments) { + switch (segment) { + case "": + break; + case ".": + break; + case "..": + normalizedSegments.pop(); + break; + default: + normalizedSegments.push(segment); + break; + } + } + return (isAbsolutePath(path) ? "/" : "") + normalizedSegments.join("/"); +} + +export function dirOf(path: string) { + // Return everything before the final slash + const lastSlash = path.lastIndexOf("/"); + return path.substring(0, lastSlash); +}