feat: always resolve path at runtime

This commit is contained in:
2026-01-14 21:33:18 +08:00
parent 62abfff439
commit b6a6630a93
5 changed files with 29 additions and 72 deletions

View File

@@ -43,7 +43,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
Ir::Null(_) => "null".to_string(),
Ir::Str(s) => s.val.escape_quote(),
Ir::Path(p) => {
// Path needs runtime resolution for interpolated paths
// Path needs runtime resolution
let path_expr = ctx.get_ir(p.expr).compile(ctx);
format!("Nix.resolvePath({})", path_expr)
}

View File

@@ -231,10 +231,6 @@ impl DowngradeContext for DowngradeCtx<'_> {
f(guard.as_ctx())
}
fn get_current_dir(&self) -> std::path::PathBuf {
self.ctx.get_current_dir()
}
fn push_dep_tracker(&mut self, slots: &[ExprId]) {
let mut graph = Graph::new();
let mut expr_to_node = HashMap::new();

View File

@@ -38,8 +38,6 @@ pub trait DowngradeContext {
where
F: FnOnce(&mut Self) -> R;
fn get_current_dir(&self) -> std::path::PathBuf;
fn push_dep_tracker(&mut self, slots: &[ExprId]);
fn push_dep_tracker_with_owner(&mut self, slots: &[ExprId], owner: ExprId);
fn get_current_binding(&self) -> Option<ExprId>;

View File

@@ -2,28 +2,10 @@
#![allow(clippy::unwrap_used)]
use rnix::ast::{self, Expr, HasEntry};
use std::path::{Component, Path as StdPath, PathBuf};
use crate::error::{Error, Result};
use super::*;
fn normalize_path(path: &StdPath) -> String {
let mut normalized = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(p) => normalized.push(p.as_os_str()),
Component::RootDir => normalized.push("/"),
Component::CurDir => {}
Component::ParentDir => {
normalized.pop();
}
Component::Normal(c) => normalized.push(c),
}
}
normalized.to_string_lossy().to_string()
}
pub trait Downgrade<Ctx: DowngradeContext> {
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId>;
}
@@ -75,46 +57,20 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::IfElse {
impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Path {
fn downgrade(self, ctx: &mut Ctx) -> Result<ExprId> {
let parts_ast: Vec<_> = self.parts().collect();
let has_interpolation = parts_ast
.iter()
.any(|part| matches!(part, ast::InterpolPart::Interpolation(_)));
let parts = if !has_interpolation {
// Resolve at compile time
let path_str: String = parts_ast
.into_iter()
.filter_map(|part| match part {
ast::InterpolPart::Literal(lit) => Some(lit.to_string()),
_ => None,
})
.collect();
let resolved_path = if path_str.starts_with('/') {
normalize_path(&std::path::PathBuf::from(&path_str))
} else {
let current_dir = ctx.get_current_dir();
normalize_path(&current_dir.join(&path_str))
};
vec![ctx.new_expr(Str { val: resolved_path }.to_ir())]
} else {
// Resolve at runtime
parts_ast
.into_iter()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(
Str {
val: lit.to_string(),
}
.to_ir(),
)),
ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(ctx)
let parts = self
.parts()
.map(|part| match part {
ast::InterpolPart::Literal(lit) => Ok(ctx.new_expr(
Str {
val: lit.to_string(),
}
})
.collect::<Result<Vec<_>>>()?
};
.to_ir(),
)),
ast::InterpolPart::Interpolation(interpol) => {
interpol.expr().unwrap().downgrade(ctx)
}
})
.collect::<Result<Vec<_>>>()?;
let expr = if parts.len() == 1 {
parts.into_iter().next().unwrap()

View File

@@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::marker::PhantomData;
use std::ops::DerefMut;
use std::path::PathBuf;
use std::path::{Component, PathBuf};
use std::sync::Once;
use deno_core::{Extension, ExtensionFileSource, JsRuntime, OpState, RuntimeOptions, v8};
@@ -125,13 +125,20 @@ fn op_resolve_path<Ctx: RuntimeCtx>(
}
// Resolve relative path against current file directory (or CWD)
let current_dir = ctx.get_current_dir();
Ok(current_dir
.join(&path)
.canonicalize()
.map(|p| p.to_string_lossy().to_string())
.map_err(|e| format!("Failed to resolve path {}: {}", path, e))?)
let current_dir = ctx.get_current_dir().join(&path);
let mut normalized = PathBuf::new();
for component in current_dir.components() {
match component {
Component::Prefix(p) => normalized.push(p.as_os_str()),
Component::RootDir => normalized.push("/"),
Component::CurDir => {}
Component::ParentDir => {
normalized.pop();
}
Component::Normal(c) => normalized.push(c),
}
}
Ok(normalized.to_string_lossy().to_string())
}
#[deno_core::op2]