Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 264 additions & 7 deletions src/rules/convert_require/rojo_sourcemap.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::{Path, PathBuf};
use std::path::{self, Component, Path, PathBuf};

use serde::{Deserialize, Serialize};

Expand All @@ -23,6 +23,40 @@ struct RojoSourcemapNode {
parent_id: NodeId,
}

pub fn normalize_path(path: &Path) -> PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
PathBuf::from(c.as_os_str())
} else {
PathBuf::new()
};

for component in components {
match component {
Component::Prefix(..) => unreachable!(),
Component::RootDir => {
ret.push(Component::RootDir);
}
Component::CurDir => {}
Component::ParentDir => {
if ret.ends_with(Component::ParentDir) {
ret.push(Component::ParentDir);
} else {
let popped = ret.pop();
if !popped && !ret.has_root() {
ret.push(Component::ParentDir);
}
}
}
Component::Normal(c) => {
ret.push(c);
}
}
}
ret
}

impl RojoSourcemapNode {
fn initialize(mut self, relative_to: &Path) -> Self {
let mut queue = vec![&mut self];
Expand All @@ -31,7 +65,9 @@ impl RojoSourcemapNode {
while let Some(node) = queue.pop() {
node.id = index;
for file_path in &mut node.file_paths {
*file_path = utils::normalize_path(relative_to.join(&file_path));
if file_path.is_relative() {
*file_path = utils::normalize_path(relative_to.join(&file_path));
}
}
for child in &mut node.children {
child.parent_id = index;
Expand Down Expand Up @@ -128,8 +164,25 @@ impl RojoSourcemap {
let from_file = from_file.as_ref();
let target_file = target_file.as_ref();

let binding = from_file.to_path_buf().join(target_file);

let normalized = normalize_path(binding.as_path());

let from_node = self.find_node(from_file)?;
let target_node = self.find_node(target_file)?;
let target_node = self.find_node(if from_file.is_absolute() {
if from_file.is_absolute() {
log::trace!(
"in absolute rqeuire mode, normalized: {} -> {}",
from_file.display(),
normalized.display()
);
normalized.as_path()
} else {
target_file
}
} else {
target_file
})?;

let from_ancestors = self.hierarchy(from_node);
let target_ancestors = self.hierarchy(target_node);
Expand Down Expand Up @@ -219,10 +272,17 @@ impl RojoSourcemap {
ids
}

fn find_node(&self, path: &Path) -> Option<&RojoSourcemapNode> {
self.root_node
.iter()
.find(|node| node.file_paths.iter().any(|file_path| file_path == path))
fn find_node(&self, target_path: &Path) -> Option<&RojoSourcemapNode> {
self.root_node.iter().find(|node| {
node.file_paths.iter().any(|file_path| {
if file_path.is_absolute() {
file_path.to_path_buf()
== path::absolute(target_path).expect("failed to convert")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to avoid calling path::absolute since this does not work well when darklua runs in-memory. Ideally we'd pre-compute all we need inside the initialize method.

} else {
file_path == target_path
}
})
})
}
}

Expand Down Expand Up @@ -277,6 +337,8 @@ mod test {
);
}

// Relative

#[test]
fn from_sibling_to_sibling_module() {
let sourcemap = new_sourcemap(
Expand Down Expand Up @@ -395,5 +457,200 @@ mod test {
script_path(&["parent"])
);
}

// Absolute

#[test]
fn abs_from_sibling_to_sibling_module() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.lua", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "main",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/main.lua"]
},
{
"name": "value",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/value.lua"]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path(
"C:/projects/thing/src/main.lua",
"C:/projects/thing/src/value.lua"
)
.unwrap(),
script_path(&["parent", "value"])
);
}

#[test]
fn abs_from_sibling_to_nested_sibling_module() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.lua", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "main",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/main.lua"]
},
{
"name": "Lib",
"className": "Folder",
"children": [
{
"name": "format",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Lib/format.lua"]
}
]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path(
"C:/projects/thing/src/main.lua",
"C:/projects/thing/src/Lib/format.lua"
)
.unwrap(),
script_path(&["parent", "Lib", "format"])
);
}

#[test]
fn abs_from_child_require_parent() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.lua", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "main",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/main.lua"]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path(
"C:/projects/thing/src/main.lua",
"C:/projects/thing/src/init.lua"
)
.unwrap(),
script_path(&["parent"])
);
}

#[test]
fn abs_from_child_require_parent_nested() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.lua", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "Sub",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/init.lua"],
"children": [
{
"name": "test",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/test.lua"]
}
]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path(
"C:/projects/thing/src/Sub/test.lua",
"C:/projects/thing/src/Sub/init.lua"
)
.unwrap(),
script_path(&["parent"])
);
}

#[test]
fn rel_from_absolute() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.luau", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "Sub",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/init.luau"],
"children": [
{
"name": "test",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/test.luau"]
}
]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path("C:/projects/thing/src/Sub/test.luau", "../init.luau")
.unwrap(),
script_path(&["parent"])
);
}

#[test]
fn nested_rel_from_absolute() {
let sourcemap = new_sourcemap(
r#"{
"name": "Project",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/init.luau", "C:/projects/thing/default.project.json"],
"children": [
{
"name": "Sub",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/init.luau"],
"children": [
{
"name": "test",
"className": "ModuleScript",
"filePaths": ["C:/projects/thing/src/Sub/test.luau"]
}
]
}
]
}"#,
);
pretty_assertions::assert_eq!(
sourcemap
.get_instance_path("C:/projects/thing/src/Sub/test.luau", "../init.luau")
.unwrap(),
script_path(&["parent"])
);
}
}
}
4 changes: 4 additions & 0 deletions src/rules/require/path_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ pub(crate) fn get_relative_path(
source_path: &Path,
use_current_dir_prefix: bool,
) -> Result<Option<PathBuf>, DarkluaError> {
if require_path.is_absolute() {
return Ok(Some(require_path.to_path_buf()));
}

let source_parent = get_relative_parent_path(source_path);

log::trace!(
Expand Down
Loading