1
- use std:: {
2
- fs:: Metadata ,
3
- path:: { MAIN_SEPARATOR , PathBuf } ,
4
- sync:: LazyLock ,
5
- } ;
1
+ use std:: path:: { Path , PathBuf } ;
6
2
7
- use anyhow:: { Result , anyhow , bail } ;
3
+ use anyhow:: { Context , Result , anyhow } ;
8
4
use console:: style;
9
5
use directories:: UserDirs ;
10
6
11
- const LUNE_COMMENT_PREFIX : & str = "-->" ;
7
+ use lune_utils :: path :: { LuauFilePath , LuauModulePath , get_current_dir } ;
12
8
13
- static ERR_MESSAGE_HELP_NOTE : LazyLock < String > = LazyLock :: new ( || {
14
- format ! (
15
- "To run this file, either:\n {}\n {}" ,
16
- format_args!(
17
- "{} rename it to use a {} or {} extension" ,
18
- style( "-" ) . dim( ) ,
19
- style( ".luau" ) . blue( ) ,
20
- style( ".lua" ) . blue( )
21
- ) ,
22
- format_args!(
23
- "{} pass it as an absolute path instead of relative" ,
24
- style( "-" ) . dim( )
25
- ) ,
26
- )
27
- } ) ;
9
+ const LUNE_COMMENT_PREFIX : & str = "-->" ;
28
10
29
11
/**
30
- Discovers a script file path based on a given script name.
31
-
32
- Script discovery is done in several steps here for the best possible user experience:
33
-
34
- 1. If we got a file that definitely exists, make sure it is either
35
- - using an absolute path
36
- - has the lua or luau extension
37
- 2. If we got a directory, check if it has an `init` file to use, and if it doesn't, let the user know
38
- 3. If we got an absolute path, don't check any extensions, just let the user know it didn't exist
39
- 4. If we got a relative path with no extension, also look for a file with a lua or luau extension
40
- 5. No other options left, the file simply did not exist
12
+ Discovers a script file path based on a given module path *or* file path.
41
13
42
- This behavior ensures that users can do pretty much whatever they want if they pass in an absolute
43
- path, and that they then have control over script discovery behavior, whereas if they pass in
44
- a relative path we will instead try to be as permissive as possible for user-friendliness
14
+ See the documentation for [`LuauModulePath`] for more information about
15
+ what a module path vs a script path is.
45
16
*/
46
- pub fn discover_script_path ( path : impl AsRef < str > , in_home_dir : bool ) -> Result < PathBuf > {
47
- // NOTE: We don't actually support any platforms without home directories,
48
- // but just in case the user has some strange configuration and it cannot
49
- // be found we should at least throw a nice error instead of panicking
50
- let path = path. as_ref ( ) ;
51
- let file_path = if in_home_dir {
52
- match UserDirs :: new ( ) {
53
- Some ( dirs) => dirs. home_dir ( ) . join ( path) ,
54
- None => {
55
- bail ! (
56
- "No file was found at {}\n The home directory does not exist" ,
57
- style( path) . yellow( )
58
- )
59
- }
60
- }
17
+ pub fn discover_script_path ( path : impl Into < PathBuf > , in_home_dir : bool ) -> Result < PathBuf > {
18
+ // First, for legacy compatibility, we will strip any lua/luau file extension,
19
+ // and if the entire file stem is simply "init", we will get rid of that too
20
+ // This lets users pass "dir/init.luau" and have it resolve to simply "dir",
21
+ // which is a valid luau module path, while "dir/init.luau" is not
22
+ let path = LuauModulePath :: strip ( path) ;
23
+
24
+ // If we got an absolute path, we should not modify it,
25
+ // otherwise we should either resolve against home or cwd
26
+ let path = if path. is_absolute ( ) {
27
+ path
28
+ } else if in_home_dir {
29
+ UserDirs :: new ( )
30
+ . context ( "Missing home directory" ) ?
31
+ . home_dir ( )
32
+ . join ( path)
61
33
} else {
62
- PathBuf :: from ( path)
34
+ get_current_dir ( ) . join ( path)
63
35
} ;
64
- // NOTE: We use metadata directly here to try to
65
- // avoid accessing the file path more than once
66
- let file_meta = file_path. metadata ( ) ;
67
- let is_file = file_meta. as_ref ( ) . is_ok_and ( Metadata :: is_file) ;
68
- let is_dir = file_meta. as_ref ( ) . is_ok_and ( Metadata :: is_dir) ;
69
- let is_abs = file_path. is_absolute ( ) ;
70
- let ext = file_path. extension ( ) ;
71
- if is_file {
72
- if is_abs {
73
- Ok ( file_path)
74
- } else if let Some ( ext) = file_path. extension ( ) {
75
- match ext {
76
- e if e == "lua" || e == "luau" => Ok ( file_path) ,
77
- _ => Err ( anyhow ! (
78
- "A file was found at {} but it uses the '{}' file extension\n {}" ,
79
- style( file_path. display( ) ) . green( ) ,
80
- style( ext. to_string_lossy( ) ) . blue( ) ,
81
- * ERR_MESSAGE_HELP_NOTE
82
- ) ) ,
83
- }
84
- } else {
85
- Err ( anyhow ! (
86
- "A file was found at {} but it has no file extension\n {}" ,
87
- style( file_path. display( ) ) . green( ) ,
88
- * ERR_MESSAGE_HELP_NOTE
89
- ) )
90
- }
91
- } else if is_dir {
92
- match (
93
- discover_script_path ( format ! ( "{path}/init.luau" ) , in_home_dir) ,
94
- discover_script_path ( format ! ( "{path}/init.lua" ) , in_home_dir) ,
95
- ) {
96
- ( Ok ( path) , _) | ( _, Ok ( path) ) => Ok ( path) ,
97
- _ => Err ( anyhow ! (
98
- "No file was found at {}, found a directory without an init file" ,
99
- style( file_path. display( ) ) . yellow( )
36
+
37
+ // The rest of the logic should follow Luau module path resolution rules
38
+ match LuauModulePath :: resolve ( & path) {
39
+ Err ( e) => Err ( anyhow ! (
40
+ "Failed to resolve script at path {} ({})" ,
41
+ style( path. display( ) ) . yellow( ) ,
42
+ style( format!( "{e:?}" ) ) . red( )
43
+ ) ) ,
44
+ Ok ( m) => match m. target ( ) {
45
+ LuauFilePath :: File ( f) => Ok ( f. clone ( ) ) ,
46
+ LuauFilePath :: Directory ( _) => Err ( anyhow ! (
47
+ "Failed to resolve script at path {}\
48
+ \n The path is a directory without an init file",
49
+ style( path. display( ) ) . yellow( )
100
50
) ) ,
101
- }
102
- } else if is_abs && !in_home_dir {
103
- Err ( anyhow ! (
104
- "No file was found at {}" ,
105
- style( file_path. display( ) ) . yellow( )
106
- ) )
107
- } else if ext. is_none ( ) {
108
- let file_path_lua = file_path. with_extension ( "lua" ) ;
109
- let file_path_luau = file_path. with_extension ( "luau" ) ;
110
- if file_path_lua. is_file ( ) {
111
- Ok ( file_path_lua)
112
- } else if file_path_luau. is_file ( ) {
113
- Ok ( file_path_luau)
114
- } else {
115
- Err ( anyhow ! (
116
- "No file was found at {}" ,
117
- style( file_path. display( ) ) . yellow( )
118
- ) )
119
- }
120
- } else {
121
- Err ( anyhow ! (
122
- "No file was found at {}" ,
123
- style( file_path. display( ) ) . yellow( )
124
- ) )
51
+ } ,
125
52
}
126
53
}
127
54
@@ -134,22 +61,24 @@ pub fn discover_script_path(path: impl AsRef<str>, in_home_dir: bool) -> Result<
134
61
135
62
Behavior is otherwise exactly the same as for `discover_script_file_path`.
136
63
*/
137
- pub fn discover_script_path_including_lune_dirs ( path : & str ) -> Result < PathBuf > {
64
+ pub fn discover_script_path_including_lune_dirs ( path : impl AsRef < Path > ) -> Result < PathBuf > {
65
+ let path: & Path = path. as_ref ( ) ;
138
66
match discover_script_path ( path, false ) {
139
67
Ok ( path) => Ok ( path) ,
140
68
Err ( e) => {
141
69
// If we got any absolute path it means the user has also
142
70
// told us to not look in any special relative directories
143
71
// so we should error right away with the first err message
144
- if PathBuf :: from ( path) . is_absolute ( ) {
72
+ if path. is_absolute ( ) {
145
73
return Err ( e) ;
146
74
}
75
+
147
76
// Otherwise we take a look in relative lune and .lune
148
77
// directories + the home directory for the current user
149
- let res = discover_script_path ( format ! ( "lune{MAIN_SEPARATOR}{path}" ) , false )
150
- . or_else ( |_| discover_script_path ( format ! ( ".lune{MAIN_SEPARATOR}{path}" ) , false ) )
151
- . or_else ( |_| discover_script_path ( format ! ( "lune{MAIN_SEPARATOR}{path}" ) , true ) )
152
- . or_else ( |_| discover_script_path ( format ! ( ".lune{MAIN_SEPARATOR}{path}" ) , true ) ) ;
78
+ let res = discover_script_path ( Path :: new ( "lune" ) . join ( path ) , false )
79
+ . or_else ( |_| discover_script_path ( Path :: new ( ".lune" ) . join ( path ) , false ) )
80
+ . or_else ( |_| discover_script_path ( Path :: new ( "lune" ) . join ( path ) , true ) )
81
+ . or_else ( |_| discover_script_path ( Path :: new ( ".lune" ) . join ( path ) , true ) ) ;
153
82
154
83
match res {
155
84
// NOTE: The first error message is generally more
0 commit comments