mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Add init file discovery in JobThreadContext and enhance script renaming logic
- Introduced a new method `find_init_file` to locate the first `init.*.luau` or `init.*.lua` file within a directory-format script folder. - Updated the script renaming logic to handle directory-format scripts correctly by renaming the init file instead of the directory itself. - Enhanced logging to provide warnings when no init file is found, improving debugging capabilities during class name changes. - Adjusted related file handling to ensure proper file path management during script transitions.
This commit is contained in:
parent
f4f07744c7
commit
f38c96e12b
2 changed files with 111 additions and 45 deletions
|
|
@ -2,7 +2,7 @@ use crossbeam_channel::{select, Receiver, RecvError, Sender};
|
||||||
use jod_thread::JoinHandle;
|
use jod_thread::JoinHandle;
|
||||||
use memofs::{IoResultExt, Vfs, VfsEvent};
|
use memofs::{IoResultExt, Vfs, VfsEvent};
|
||||||
use rbx_dom_weak::types::{Ref, Variant};
|
use rbx_dom_weak::types::{Ref, Variant};
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
|
|
@ -134,6 +134,31 @@ struct JobThreadContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JobThreadContext {
|
impl JobThreadContext {
|
||||||
|
/// Find the init file inside a directory-format script folder.
|
||||||
|
/// Returns the path to the first `init.*.luau` or `init.*.lua` found.
|
||||||
|
fn find_init_file(dir: &Path) -> Option<PathBuf> {
|
||||||
|
// Check known init file names in priority order
|
||||||
|
let candidates = [
|
||||||
|
"init.luau",
|
||||||
|
"init.server.luau",
|
||||||
|
"init.client.luau",
|
||||||
|
"init.local.luau",
|
||||||
|
"init.plugin.luau",
|
||||||
|
"init.legacy.luau",
|
||||||
|
"init.lua",
|
||||||
|
"init.server.lua",
|
||||||
|
"init.client.lua",
|
||||||
|
"init.local.lua",
|
||||||
|
];
|
||||||
|
for name in &candidates {
|
||||||
|
let candidate = dir.join(name);
|
||||||
|
if candidate.exists() {
|
||||||
|
return Some(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes and applies patches to the DOM for a given file path.
|
/// Computes and applies patches to the DOM for a given file path.
|
||||||
///
|
///
|
||||||
/// This function finds the nearest ancestor to the given path that has associated instances
|
/// This function finds the nearest ancestor to the given path that has associated instances
|
||||||
|
|
@ -538,52 +563,78 @@ impl JobThreadContext {
|
||||||
);
|
);
|
||||||
|
|
||||||
if old_is_script && new_is_script && path.exists() {
|
if old_is_script && new_is_script && path.exists() {
|
||||||
// Script-to-script transition: rename the file extension
|
// Script-to-script transition: rename the file extension.
|
||||||
if let Some(parent) = path.parent() {
|
// For directory-format scripts, the path is the directory
|
||||||
let name = instance.name();
|
// (e.g., src/MyModule/), not the init file inside. We must
|
||||||
let new_suffix = match new_class.as_str() {
|
// find and rename the init file, not the directory.
|
||||||
"ModuleScript" => "",
|
let (actual_file, file_parent) = if path.is_dir() {
|
||||||
"Script" => ".server",
|
// Find the init file inside the directory
|
||||||
"LocalScript" => ".local",
|
let init_file = Self::find_init_file(path);
|
||||||
_ => "",
|
match init_file {
|
||||||
};
|
Some(f) => (f.clone(), path.to_path_buf()),
|
||||||
let is_init = path
|
None => {
|
||||||
.file_name()
|
log::warn!(
|
||||||
.and_then(|f| f.to_str())
|
"Cannot change ClassName for directory {} \
|
||||||
.map(|f| f.starts_with("init."))
|
— no init file found inside",
|
||||||
.unwrap_or(false);
|
path.display()
|
||||||
|
|
||||||
let new_file_name = if is_init {
|
|
||||||
if new_suffix.is_empty() {
|
|
||||||
"init.luau".to_string()
|
|
||||||
} else {
|
|
||||||
format!("init{}.luau", new_suffix)
|
|
||||||
}
|
|
||||||
} else if new_suffix.is_empty() {
|
|
||||||
format!("{}.luau", name)
|
|
||||||
} else {
|
|
||||||
format!("{}{}.luau", name, new_suffix)
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_path = parent.join(&new_file_name);
|
|
||||||
if new_path != *path {
|
|
||||||
log::info!(
|
|
||||||
"Two-way sync: Changing class {} -> {}, \
|
|
||||||
renaming {} -> {}",
|
|
||||||
old_class,
|
|
||||||
new_class,
|
|
||||||
path.display(),
|
|
||||||
new_path.display()
|
|
||||||
);
|
|
||||||
if let Err(err) = fs::rename(path, &new_path) {
|
|
||||||
log::error!(
|
|
||||||
"Failed to rename {:?} to {:?}: {}",
|
|
||||||
path,
|
|
||||||
new_path,
|
|
||||||
err
|
|
||||||
);
|
);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
path.clone(),
|
||||||
|
path.parent()
|
||||||
|
.unwrap_or(path.as_path())
|
||||||
|
.to_path_buf(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = instance.name();
|
||||||
|
let new_suffix = match new_class.as_str() {
|
||||||
|
"ModuleScript" => "",
|
||||||
|
"Script" => ".server",
|
||||||
|
"LocalScript" => ".local",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
let is_init = actual_file
|
||||||
|
.file_name()
|
||||||
|
.and_then(|f| f.to_str())
|
||||||
|
.map(|f| f.starts_with("init."))
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let new_file_name = if is_init {
|
||||||
|
if new_suffix.is_empty() {
|
||||||
|
"init.luau".to_string()
|
||||||
|
} else {
|
||||||
|
format!("init{}.luau", new_suffix)
|
||||||
|
}
|
||||||
|
} else if new_suffix.is_empty() {
|
||||||
|
format!("{}.luau", name)
|
||||||
|
} else {
|
||||||
|
format!("{}{}.luau", name, new_suffix)
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_path = file_parent.join(&new_file_name);
|
||||||
|
if new_path != actual_file {
|
||||||
|
log::info!(
|
||||||
|
"Two-way sync: Changing class {} -> {}, \
|
||||||
|
renaming {} -> {}",
|
||||||
|
old_class,
|
||||||
|
new_class,
|
||||||
|
actual_file.display(),
|
||||||
|
new_path.display()
|
||||||
|
);
|
||||||
|
if let Err(err) =
|
||||||
|
fs::rename(&actual_file, &new_path)
|
||||||
|
{
|
||||||
|
log::error!(
|
||||||
|
"Failed to rename {:?} to {:?}: {}",
|
||||||
|
actual_file,
|
||||||
|
new_path,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if old_is_script != new_is_script {
|
} else if old_is_script != new_is_script {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
|
|
|
||||||
|
|
@ -2057,7 +2057,22 @@ impl ApiService {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.context("Instance has no filesystem path")?;
|
.context("Instance has no filesystem path")?;
|
||||||
|
|
||||||
let inst_path = instigating_source.path();
|
// ProjectNode instances are defined in the project file. Writing meta
|
||||||
|
// JSON to the project file path would corrupt it. Only filesystem-backed
|
||||||
|
// instances (InstigatingSource::Path) can have properties persisted.
|
||||||
|
let inst_path = match instigating_source {
|
||||||
|
InstigatingSource::Path(p) => p.as_path(),
|
||||||
|
InstigatingSource::ProjectNode { name, .. } => {
|
||||||
|
log::warn!(
|
||||||
|
"Cannot persist non-Source properties for instance '{}' (id: {:?}) — \
|
||||||
|
it is defined in a project file. Edit the project file directly.",
|
||||||
|
name,
|
||||||
|
update.id
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let class_name = instance.class_name();
|
let class_name = instance.class_name();
|
||||||
let is_script = matches!(
|
let is_script = matches!(
|
||||||
class_name.as_str(),
|
class_name.as_str(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue