Repository: wezterm/wezterm (language: rust)

Issue:
`wezterm.plugin.require()` errors when there's a `.` in the directory name
### What Operating System(s) are you seeing this problem on?

Linux X11

### Which Wayland compositor or X11 Window manager(s) are you using?

i3 version 4.21.1 (2022-10-24) on NixOS

### WezTerm version

2d05f8f1f24db6370f0791e9ec3a98ad38cf8b4d

### Did you try the latest nightly build to see if the issue is better (or worse!) than your current version?

Yes, and I updated the version box above to show the version of the nightly that I tried

### Describe the bug

I tried to make a [plugin](https://github.com/nekowinston/wezterm.bar) that conforms to the [experimental plugin spec](https://github.com/wez/wezterm/commit/e4ae8a844d8feaa43e1de34c5cc8b4f07ce525dd).

When I'm loading my plugin as `https://github.com/nekowinston/wezterm.bar`, I'm getting the following stack traceback:
```
callback error
stack traceback:
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:34: in main chunk
caused by: runtime error: module
'httpssCssZssZsgithub.comsZsnekowinstonsZswezterm.bar' not found:
        no field package.preload['httpssCssZssZsgithub.comsZsnekowinstonsZswezterm.bar']
        no file '/home/winston/.config/wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar.lua'
        no file '/home/winston/.config/wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar/init.lua'
        no file '/run/user/1000/wezterm/plugins/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar/plugin/init.lua'
        no file '/home/winston/.wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar.lua'
        no file '/home/winston/.wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar/init.lua'
        no file '/usr/local/share/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar.lua'
        no file '/usr/local/share/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar/init.lua'
        no file '/usr/local/lib/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar.lua'
        no file '/usr/local/lib/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm/bar/init.lua'
        no file './httpssCssZssZsgithub/comsZsnekowinstonsZswezterm/bar.lua'
        no file './httpssCssZssZsgithub/comsZsnekowinstonsZswezterm/bar/init.lua'

        can't load C modules in safe mode
stack traceback:
        [C]: in ?
        [C]: in function 'require'
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:34: in main chunk
```

This also happens when using a local directory, named `wezterm.bar`:
```lua
local wezterm = require("wezterm")

local c = wezterm.config_builder()

c.color_scheme = "Catppuccin Mocha"
wezterm.plugin.require("/home/winston/Code/wezterm.bar").apply_to_config(c)

return c
```

```
callback error
stack traceback:
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:34: in main chunk
caused by: runtime error: module 'sZshomesZswinstonsZsCodesZswezterm.bar'
not found:
        no field package.preload['sZshomesZswinstonsZsCodesZswezterm.bar']
        no file '/home/winston/.config/wezterm/sZshomesZswinstonsZsCodesZswezterm/
bar.lua'
        no file '/home/winston/.config/wezterm/sZshomesZswinstonsZsCodesZswezterm/
bar/init.lua'
        no file '/run/user/1000/wezterm/plugins/sZshomesZswinstonsZsCodesZswezterm/
bar/plugin/init.lua'
        no file '/home/winston/.wezterm/sZshomesZswinstonsZsCodesZswezterm/bar.lua'
        no file '/home/winston/.wezterm/sZshomesZswinstonsZsCodesZswezterm/bar/
init.lua'
        no file '/usr/local/share/lua/5.4/sZshomesZswinstonsZsCodesZswezterm/bar.lua'
        no file '/usr/local/share/lua/5.4/sZshomesZswinstonsZsCodesZswezterm/bar/
init.lua'
        no file '/usr/local/lib/lua/5.4/sZshomesZswinstonsZsCodesZswezterm/bar.lua'
        no file '/usr/local/lib/lua/5.4/sZshomesZswinstonsZsCodesZswezterm/bar/
init.lua'
        no file './sZshomesZswinstonsZsCodesZswezterm/bar.lua'
        no file './sZshomesZswinstonsZsCodesZswezterm/bar/init.lua'

        can't load C modules in safe mode
stack traceback:
        [C]: in ?
        [C]: in function 'require'
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:34: in main chunk
```

### To Reproduce

Use the minimal config I provided.

### Configuration

Minimal config:
```lua
local wezterm = require("wezterm")

local c = wezterm.config_builder()
-- the plugin is currently made for Catppuccin
c.color_scheme = "Catppuccin Mocha"

wezterm.plugin
  .require("https://github.com/nekowinston/wezterm.bar")
  .apply_to_config(c)

return c
```

### Expected Behavior

The same behavior as when using `wezterm-bar` for the name - the plugin loading and applying to the config.

### Logs

_No response_

### Anything else?

When I'm using a local path and the folder is named `wezterm-bar`, it works!
```lua
local wezterm = require("wezterm")

local c = wezterm.config_builder()

c.color_scheme = "Catppuccin Mocha"
wezterm.plugin.require("/home/winston/Code/wezterm-bar").apply_to_config(c)

return c
```


Discussion / hints:
I just renamed my plugin repository to `wezterm-bar` to see if that fixes the remote part of the issue:
```
> wezterm.plugin.list()
[
    {
        "component": "httpssCssZssZsgithub.comsZsnekowinstonsZswezterm-bar",
        "url": "https://github.com/nekowinston/wezterm-bar",
    },
]
```
Which clones it as:
```
> tree /run/user/1000/wezterm/plugins/
/run/user/1000/wezterm/plugins/
└── httpssCssZssZsgithub.comsZsnekowinstonsZswezterm-bar
    ├── plugin
    │   └── init.lua
    └── README.md
```

And I'm still getting the same error:
```
callback error
stack traceback:
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:33: in main chunk
caused by: runtime error: module
'httpssCssZssZsgithub.comsZsnekowinstonsZswezterm-bar' not found:
        no field package.preload['httpssCssZssZsgithub.comsZsnekowinstonsZswezterm-
bar']
        no file '/home/winston/.config/wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar.lua'
        no file '/home/winston/.config/wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar/init.lua'
        no file '/run/user/1000/wezterm/plugins/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar/plugin/init.lua'
        no file '/home/winston/.wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar.lua'
        no file '/home/winston/.wezterm/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar/init.lua'
        no file '/usr/local/share/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar.lua'
        no file '/usr/local/share/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar/init.lua'
        no file '/usr/local/lib/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar.lua'
        no file '/usr/local/lib/lua/5.4/httpssCssZssZsgithub/
comsZsnekowinstonsZswezterm-bar/init.lua'
        no file './httpssCssZssZsgithub/comsZsnekowinstonsZswezterm-bar.lua'
        no file './httpssCssZssZsgithub/comsZsnekowinstonsZswezterm-bar/init.lua'

        can't load C modules in safe mode
stack traceback:
        [C]: in ?
        [C]: in function 'require'
        [C]: in field 'require'
        [string "/home/winston/.config/wezterm/wezterm.lua"]:33: in main chunk
```
I think it also impacts the dot in the `httpssCssZssZsgithub.com` part.
This is not a wezterm issue, this is a lua "issue".

Lua uses `.` as the delimiter in module pathing (similar to python, java, etc). So what is happening here is lua is attempting to import the module `bar` from the module `wezterm` (`wezterm.bar`) which is not what you _want_ to tell it to do.

The "fix" is to load your module in a different manner.

EG: https://stackoverflow.com/a/59597110/2104990
>  require looks for files using loaders, you can add a custom loader by inserting the function into package.loaders.
> 
> Your custom loader might look like this:
> ```lua
> local function load(modulename)
>   local errmsg = ""
>   for path in string.gmatch(package.path, "([^;]+)") do
>     local filename = string.gsub(path, "%?", modulename)
>     local file = io.open(filename, "rb")
>     if file then
>       -- Compile and return the module
>       return assert(loadstring(assert(file:read("*a")), filename))
>     end
>     errmsg = errmsg.."\n\tno file '"..filename.."' (checked with custom loader)"
>   end
>   return errmsg
> end
> 
> table.insert(package.loaders, 2, load) -- this will run before the standard loader, if you want it to
>                                        -- run after you can call table.insert(package.loaders, load)
> ```

Related: [LuaModulesLoader](http://lua-users.org/wiki/LuaModulesLoader)
Yes, I know *why* it's happening, I'm suggesting that the clone function of `wezterm.plugin.update_all()` changes the path (on remote urls, since it's working when sourced locally and there is no dot), so that it (for example) replaces `.` with `_`.
https://github.com/wez/wezterm/blob/e4ae8a844d8feaa43e1de34c5cc8b4f07ce525dd/lua-api-crates/plugin/src/lib.rs#L243-L255

From the [commit I linked to](https://github.com/wez/wezterm/commit/e4ae8a844d8feaa43e1de34c5cc8b4f07ce525dd#) for the experimental spec:
> Brief usage notes here:
>
> ```lua
> local wezterm = require 'wezterm'
> local a_plugin = wezterm.plugin.require "https://github.com/owner/repo"
>
> local config = wezterm.config_builder()
>
> a_plugin.apply_to_config(config)
>
> return config
> ```
>
> The referenced repo is expected to have a `plugin/init.lua` file,
> and by convention, return a module that exports an `apply_to_config`
> function that accepts at least a config builder parameter, but may
> pass other parameters, or a lua table with a `config` field that maps
> to a config build parameter.
> [...]
> `wezterm.plugin.require` will then perform `require "NAME"`,
> and since the default `package.path` now includes the appropriate
> location from the runtime dir, the module should load.

Fix patch:
diff --git a/lua-api-crates/plugin/src/lib.rs b/lua-api-crates/plugin/src/lib.rs
index e080c5f439e..92c63cc5d21 100644
--- a/lua-api-crates/plugin/src/lib.rs
+++ b/lua-api-crates/plugin/src/lib.rs
@@ -26,7 +26,10 @@ fn compute_repo_dir(url: &str) -> String {
             ':' => {
                 dir.push_str("sCs");
             }
-            '.' | '-' | '_' => dir.push(c),
+            '.' => {
+                dir.push_str("sDs");
+            }
+            '-' | '_' => dir.push(c),
             c if c.is_alphanumeric() => dir.push(c),
             c => dir.push_str(&format!("u{}", c as u32)),
         }
@@ -244,8 +247,8 @@ mod test {
         for (input, expect) in &[
             ("foo", "foo"),
             (
-                "github.com/wez/wezterm-plugins",
-                "github.comsZswezsZswezterm-plugins",
+                "githubsDscom/wez/wezterm-plugins",
+                "githubsDscomsZswezsZswezterm-plugins",
             ),
             ("localhost:8080/repo", "localhostsCs8080sZsrepo"),
         ] {


Test patch: