Implement self alias for module requires

This commit is contained in:
Filip Tibell 2025-04-24 21:02:16 +02:00
parent a673f80c95
commit 74375ff708
No known key found for this signature in database
7 changed files with 56 additions and 5 deletions

View file

@ -55,15 +55,45 @@ impl RequireContext {
This will resolve path segments such as `./`, `../`, ..., and This will resolve path segments such as `./`, `../`, ..., and
if the resolved path is not an absolute path, will create an if the resolved path is not an absolute path, will create an
absolute path by prepending the current working directory. absolute path by prepending the current working directory.
If `resolve_as_self` is true, the given path should be a luau
module require path in the format of `@self/foo/bar/...` with the
`@self` prefix being stripped, and only `foo/bar/...` being passed.
*/ */
pub fn resolve_paths( pub fn resolve_paths(
source: impl AsRef<str>, source: impl AsRef<str>,
path: impl AsRef<str>, path: impl AsRef<str>,
resolve_as_self: bool,
) -> LuaResult<(PathBuf, PathBuf)> { ) -> LuaResult<(PathBuf, PathBuf)> {
let path = PathBuf::from(source.as_ref()) let source = PathBuf::from(source.as_ref());
let path = PathBuf::from(path.as_ref());
let is_init_module = {
let is_init = path
.file_stem()
.and_then(|stem| stem.to_str())
.is_some_and(|stem| stem.eq_ignore_ascii_case("init"));
let is_luau = is_init
&& path
.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| matches!(ext, "lua" | "luau"));
is_init && is_luau
};
let source = if is_init_module && !resolve_as_self {
source
.parent()
.ok_or_else(|| LuaError::runtime("Failed to get parent path of self"))?
.to_path_buf()
} else {
source
};
let path = source
.parent() .parent()
.ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))? .ok_or_else(|| LuaError::runtime("Failed to get parent path of source"))?
.join(path.as_ref()); .join(path);
let abs_path = clean_path_and_make_absolute(&path); let abs_path = clean_path_and_make_absolute(&path);
let rel_path = clean_path(path); let rel_path = clean_path(path);

View file

@ -80,13 +80,15 @@ async fn require(lua: Lua, (source, path): (LuaString, LuaString)) -> LuaResult<
if let Some(builtin_name) = path.strip_prefix("@lune/").map(str::to_ascii_lowercase) { if let Some(builtin_name) = path.strip_prefix("@lune/").map(str::to_ascii_lowercase) {
library::require(lua, &context, &builtin_name) library::require(lua, &context, &builtin_name)
} else if let Some(self_path) = path.strip_prefix("@self/") {
path::require(lua, &context, &source, self_path, true).await
} else if let Some(aliased_path) = path.strip_prefix('@') { } else if let Some(aliased_path) = path.strip_prefix('@') {
let (alias, path) = aliased_path.split_once('/').ok_or(LuaError::runtime( let (alias, path) = aliased_path.split_once('/').ok_or(LuaError::runtime(
"Require with custom alias must contain '/' delimiter", "Require with custom alias must contain '/' delimiter",
))?; ))?;
alias::require(lua, &context, &source, alias, path).await alias::require(lua, &context, &source, alias, path).await
} else if path.starts_with("./") || path.starts_with("../") { } else if path.starts_with("./") || path.starts_with("../") {
path::require(lua, &context, &source, &path).await path::require(lua, &context, &source, &path, false).await
} else { } else {
Err(LuaError::runtime( Err(LuaError::runtime(
"Require path must start with \"./\", \"../\" or \"@\"", "Require path must start with \"./\", \"../\" or \"@\"",

View file

@ -10,8 +10,9 @@ pub(super) async fn require(
ctx: &RequireContext, ctx: &RequireContext,
source: &str, source: &str,
path: &str, path: &str,
resolve_as_self: bool,
) -> LuaResult<LuaMultiValue> { ) -> LuaResult<LuaMultiValue> {
let (abs_path, rel_path) = RequireContext::resolve_paths(source, path)?; let (abs_path, rel_path) = RequireContext::resolve_paths(source, path, resolve_as_self)?;
require_abs_rel(lua, ctx, abs_path, rel_path).await require_abs_rel(lua, ctx, abs_path, rel_path).await
} }

View file

@ -12,4 +12,8 @@ module = require("./modules/modules")
assert(module.Foo == "Bar", "Required module did not contain correct values") assert(module.Foo == "Bar", "Required module did not contain correct values")
assert(module.Hello == "World", "Required module did not contain correct values") assert(module.Hello == "World", "Required module did not contain correct values")
module = require("./modules/self_alias")
assert(module.Foo == "Bar", "Required module did not contain correct values")
assert(module.Hello == "World", "Required module did not contain correct values")
return true return true

View file

@ -1,6 +1,6 @@
local function test(path: string) local function test(path: string)
local success, message = pcall(function() local success, message = pcall(function()
local _ = require(path) :: any local _ = require("./" .. path) :: any
end) end)
if success then if success then
error(string.format("Invalid require at path '%s' succeeded", path)) error(string.format("Invalid require at path '%s' succeeded", path))

View file

@ -0,0 +1,10 @@
local outer = require("./module")
local inner = require("@self/module")
assert(type(outer) == "table", "Outer module is not a table")
assert(type(inner) == "table", "Inner module is not a table")
assert(outer.Foo == inner.Foo, "Outer and inner modules have different Foo values")
assert(inner.Bar == outer.Bar, "Outer and inner modules have different Bar values")
return inner

View file

@ -0,0 +1,4 @@
return {
Foo = "Bar",
Hello = "World",
}