mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-24 05:03:06 +00:00
Merge pull request #57 from mrxiaozhuox/master
This commit is contained in:
commit
2e804f71b3
37 changed files with 1561 additions and 78 deletions
0
.fleet/settings.json
Normal file
0
.fleet/settings.json
Normal file
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
||||||
/target
|
/target
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
.DS_Store
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
|
@ -1 +1,6 @@
|
||||||
{}
|
{
|
||||||
|
"Lua.diagnostics.globals": [
|
||||||
|
"plugin_logger",
|
||||||
|
"PLUGIN_DOWNLOADER"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
112
Cargo.lock
generated
112
Cargo.lock
generated
|
@ -222,6 +222,15 @@ dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.11.0"
|
version = "3.11.0"
|
||||||
|
@ -493,6 +502,16 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curl"
|
name = "curl"
|
||||||
version = "0.4.44"
|
version = "0.4.44"
|
||||||
|
@ -554,6 +573,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"colored 2.0.0",
|
"colored 2.0.0",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
|
"ctrlc",
|
||||||
"dioxus-core",
|
"dioxus-core",
|
||||||
"dioxus-rsx",
|
"dioxus-rsx",
|
||||||
"dirs 4.0.0",
|
"dirs 4.0.0",
|
||||||
|
@ -565,7 +585,9 @@ dependencies = [
|
||||||
"html_parser",
|
"html_parser",
|
||||||
"hyper",
|
"hyper",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
|
"mlua",
|
||||||
"notify",
|
"notify",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -594,6 +616,7 @@ dependencies = [
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"fxhash",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"longest-increasing-subsequence",
|
"longest-increasing-subsequence",
|
||||||
|
@ -653,6 +676,12 @@ version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -878,6 +907,15 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.6"
|
||||||
|
@ -1227,6 +1265,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -1341,6 +1388,24 @@ dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lua-src"
|
||||||
|
version = "544.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "708ba3c844d5e9d38def4a09dd871c17c370f519b3c4b7261fbabe4a613a814c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "luajit-src"
|
||||||
|
version = "210.4.1+restyaa7a722"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c92879345f9a97ee140cfe2e08eff49b101533d784527d46ce6d2dc0096d27b3"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "match_cfg"
|
name = "match_cfg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1402,6 +1467,41 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mlua"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10277581090f5cb7ecf814bc611152ce4db6dc8deffcaa08e24ed4c5197d9186"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"cc",
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
"lua-src",
|
||||||
|
"luajit-src",
|
||||||
|
"mlua_derive",
|
||||||
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
"rustc-hash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mlua_derive"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9214e60d3cf1643013b107330fcd374ccec1e4ba1eef76e7e5da5e8202e71c0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.10"
|
version = "0.2.10"
|
||||||
|
@ -1420,6 +1520,18 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "5.0.0-pre.16"
|
version = "5.0.0-pre.16"
|
||||||
|
|
|
@ -46,7 +46,7 @@ walkdir = "2"
|
||||||
|
|
||||||
# tools download
|
# tools download
|
||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns"] }
|
reqwest = { version = "0.11", features = ["rustls-tls", "stream", "trust-dns", "blocking"] }
|
||||||
flate2 = "1.0.22"
|
flate2 = "1.0.22"
|
||||||
tar = "0.4.38"
|
tar = "0.4.38"
|
||||||
zip = "0.6.2"
|
zip = "0.6.2"
|
||||||
|
@ -62,7 +62,11 @@ dioxus-rsx = { git = "https://github.com/dioxuslabs/dioxus/", features = [
|
||||||
] }
|
] }
|
||||||
|
|
||||||
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
proc-macro2 = { version = "1.0", features = ["span-locations"] }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
# plugin packages
|
||||||
|
mlua = { version = "0.8.1", features = ["lua54", "vendored", "async", "send", "macros"] }
|
||||||
|
ctrlc = "3.2.3"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
|
@ -9,3 +9,10 @@
|
||||||
- [Serve](./cmd/serve.md)
|
- [Serve](./cmd/serve.md)
|
||||||
- [Clean](./cmd/clean.md)
|
- [Clean](./cmd/clean.md)
|
||||||
- [Translate](./cmd/translate.md)
|
- [Translate](./cmd/translate.md)
|
||||||
|
- [Plugin Development](./plugin/README.md)
|
||||||
|
- [API.Log](./plugin/interface/log.md)
|
||||||
|
- [API.Command](./plugin/interface/command.md)
|
||||||
|
- [API.OS](./plugin/interface/os.md)
|
||||||
|
- [API.Directories](./plugin/interface/dirs.md)
|
||||||
|
- [API.Network](./plugin/interface/network.md)
|
||||||
|
- [API.Path](./plugin/interface/path.md)
|
79
docs/src/plugin/README.md
Normal file
79
docs/src/plugin/README.md
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# CLI Plugin Development
|
||||||
|
|
||||||
|
> For Cli 0.2.0 we will add `plugin-develop` support.
|
||||||
|
|
||||||
|
Before the 0.2.0 we use `dioxus tool` to use & install some plugin, but we think that is not good for extend cli program, some people want tailwind support, some people want sass support, we can't add all this thing in to the cli source code and we don't have time to maintain a lot of tools that user request, so maybe user make plugin by themself is a good choice.
|
||||||
|
|
||||||
|
### Why Lua ?
|
||||||
|
|
||||||
|
We choose `Lua: 5.4` to be the plugin develop language, because cli plugin is not complex, just like a workflow, and user & developer can write some easy code for their plugin. We have **vendored** lua in cli program, and user don't need install lua runtime in their computer, and the lua parser & runtime doesn't take up much disk memory.
|
||||||
|
|
||||||
|
### Event Management
|
||||||
|
|
||||||
|
The plugin library have pre-define some important event you can control:
|
||||||
|
|
||||||
|
- `build.on_start`
|
||||||
|
- `build.on_finished`
|
||||||
|
- `serve.on_start`
|
||||||
|
- `serve.on_rebuild`
|
||||||
|
- `serve.on_shutdown`
|
||||||
|
|
||||||
|
### Plugin Template
|
||||||
|
|
||||||
|
```lua
|
||||||
|
package.path = library_dir .. "/?.lua"
|
||||||
|
|
||||||
|
local plugin = require("plugin")
|
||||||
|
local manager = require("manager")
|
||||||
|
|
||||||
|
-- deconstruct api functions
|
||||||
|
local log = plugin.log
|
||||||
|
|
||||||
|
-- plugin information
|
||||||
|
manager.name = "Hello Dixous Plugin"
|
||||||
|
manager.repository = "https://github.com/mrxiaozhuox/hello-dioxus-plugin"
|
||||||
|
manager.author = "YuKun Liu <mrxzx.info@gmail.com>"
|
||||||
|
manager.version = "0.0.1"
|
||||||
|
|
||||||
|
-- init manager info to plugin api
|
||||||
|
plugin.init(manager)
|
||||||
|
|
||||||
|
manager.on_init = function ()
|
||||||
|
-- when the first time plugin been load, this function will be execute.
|
||||||
|
-- system will create a `dcp.json` file to verify init state.
|
||||||
|
log.info("[plugin] Start to init plugin: " .. manager.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param info BuildInfo
|
||||||
|
manager.build.on_start = function (info)
|
||||||
|
-- before the build work start, system will execute this function.
|
||||||
|
log.info("[plugin] Build starting: " .. info.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param info BuildInfo
|
||||||
|
manager.build.on_finish = function (info)
|
||||||
|
-- when the build work is done, system will execute this function.
|
||||||
|
log.info("[plugin] Build finished: " .. info.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param info ServeStartInfo
|
||||||
|
manager.serve.on_start = function (info)
|
||||||
|
-- this function will after clean & print to run, so you can print some thing.
|
||||||
|
log.info("[plugin] Serve start: " .. info.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param info ServeRebuildInfo
|
||||||
|
manager.serve.on_rebuild = function (info)
|
||||||
|
-- this function will after clean & print to run, so you can print some thing.
|
||||||
|
local files = plugin.tool.dump(info.changed_files)
|
||||||
|
log.info("[plugin] Serve rebuild: '" .. files .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
manager.serve.on_shutdown = function ()
|
||||||
|
log.info("[plugin] Serve shutdown")
|
||||||
|
end
|
||||||
|
|
||||||
|
manager.serve.interval = 1000
|
||||||
|
|
||||||
|
return manager
|
||||||
|
```
|
21
docs/src/plugin/interface/command.md
Normal file
21
docs/src/plugin/interface/command.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Command Functions
|
||||||
|
|
||||||
|
> you can use command functions to execute some code & script
|
||||||
|
|
||||||
|
Type Define:
|
||||||
|
```
|
||||||
|
Stdio: "Inhert" | "Piped" | "Null"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `exec(commands: [string], stdout: Stdio, stderr: Stdio)`
|
||||||
|
|
||||||
|
you can use this function to run some command on the current system.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local cmd = plugin.command
|
||||||
|
|
||||||
|
manager.test = function ()
|
||||||
|
cmd.exec({"git", "clone", "https://github.com/DioxusLabs/cli-plugin-library"})
|
||||||
|
end
|
||||||
|
```
|
||||||
|
> Warning: This function don't have exception catch.
|
35
docs/src/plugin/interface/dirs.md
Normal file
35
docs/src/plugin/interface/dirs.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Dirs Functions
|
||||||
|
|
||||||
|
> you can use Dirs functions to get some directory path
|
||||||
|
|
||||||
|
|
||||||
|
### plugin_dir() -> string
|
||||||
|
|
||||||
|
You can get current plugin **root** directory path
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local path = plugin.dirs.plugin_dir()
|
||||||
|
-- example: ~/Development/DioxusCli/plugin/test-plugin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### bin_dir() -> string
|
||||||
|
|
||||||
|
You can get plugin **bin** direcotry path
|
||||||
|
|
||||||
|
Sometime you need install some binary file like `tailwind-cli` & `sass-cli` to help your plugin work, then you should put binary file in this directory.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local path = plugin.dirs.bin_dir()
|
||||||
|
-- example: ~/Development/DioxusCli/plugin/test-plugin/bin/
|
||||||
|
```
|
||||||
|
|
||||||
|
### temp_dir() -> string
|
||||||
|
|
||||||
|
You can get plugin **temp** direcotry path
|
||||||
|
|
||||||
|
Just put some temporary file in this directory.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local path = plugin.dirs.bin_dir()
|
||||||
|
-- example: ~/Development/DioxusCli/plugin/test-plugin/temp/
|
||||||
|
```
|
48
docs/src/plugin/interface/log.md
Normal file
48
docs/src/plugin/interface/log.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Log Functions
|
||||||
|
|
||||||
|
> You can use log function to print some useful log info
|
||||||
|
|
||||||
|
### Trace(info: string)
|
||||||
|
|
||||||
|
Print trace log info
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local log = plugin.log
|
||||||
|
log.trace("trace information")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug(info: string)
|
||||||
|
|
||||||
|
Print debug log info
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local log = plugin.log
|
||||||
|
log.debug("debug information")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Info(info: string)
|
||||||
|
|
||||||
|
Print info log info
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local log = plugin.log
|
||||||
|
log.info("info information")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warn(info: string)
|
||||||
|
|
||||||
|
Print warning log info
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local log = plugin.log
|
||||||
|
log.warn("warn information")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error(info: string)
|
||||||
|
|
||||||
|
Print error log info
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local log = plugin.log
|
||||||
|
log.error("error information")
|
||||||
|
```
|
34
docs/src/plugin/interface/network.md
Normal file
34
docs/src/plugin/interface/network.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# Network Functions
|
||||||
|
|
||||||
|
> you can use Network functions to download & read some data from internet
|
||||||
|
|
||||||
|
### download_file(url: string, path: string) -> boolean
|
||||||
|
|
||||||
|
This function can help you download some file from url, and it will return a *boolean* value to check the download status. (true: success | false: fail)
|
||||||
|
|
||||||
|
You need pass a target url and a local path (where you want to save this file)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- this file will download to plugin temp directory
|
||||||
|
local status = plugin.network.download_file(
|
||||||
|
"http://xxx.com/xxx.zip",
|
||||||
|
plugin.dirs.temp_dir()
|
||||||
|
)
|
||||||
|
if status != true then
|
||||||
|
log.error("Download Failed")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### clone_repo(url: string, path: string) -> boolean
|
||||||
|
|
||||||
|
This function can help you use `git clone` command (this system must have been installed git)
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local status = plugin.network.clone_repo(
|
||||||
|
"http://github.com/mrxiaozhuox/dioxus-starter",
|
||||||
|
plugin.dirs.bin_dir()
|
||||||
|
)
|
||||||
|
if status != true then
|
||||||
|
log.error("Clone Failed")
|
||||||
|
end
|
||||||
|
```
|
11
docs/src/plugin/interface/os.md
Normal file
11
docs/src/plugin/interface/os.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# OS Functions
|
||||||
|
|
||||||
|
> you can use OS functions to get some system information
|
||||||
|
|
||||||
|
### current_platform() -> string ("windows" | "macos" | "linux")
|
||||||
|
|
||||||
|
This function can help you get system & platform type:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local platform = plugin.os.current_platform()
|
||||||
|
```
|
35
docs/src/plugin/interface/path.md
Normal file
35
docs/src/plugin/interface/path.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Path Functions
|
||||||
|
|
||||||
|
> you can use path functions to operate valid path string
|
||||||
|
|
||||||
|
### join(path: string, extra: string) -> string
|
||||||
|
|
||||||
|
This function can help you extend a path, you can extend any path, dirname or filename.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local current_path = "~/hello/dioxus"
|
||||||
|
local new_path = plugin.path.join(current_path, "world")
|
||||||
|
-- new_path = "~/hello/dioxus/world"
|
||||||
|
```
|
||||||
|
|
||||||
|
### parent(path: string) -> string
|
||||||
|
|
||||||
|
This function will return `path` parent-path string, back to the parent.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local current_path = "~/hello/dioxus"
|
||||||
|
local new_path = plugin.path.parent(current_path)
|
||||||
|
-- new_path = "~/hello/"
|
||||||
|
```
|
||||||
|
|
||||||
|
### exists(path: string) -> boolean
|
||||||
|
|
||||||
|
This function can check some path (dir & file) is exists.
|
||||||
|
|
||||||
|
### is_file(path: string) -> boolean
|
||||||
|
|
||||||
|
This function can check some path is a exist file.
|
||||||
|
|
||||||
|
### is_dir(path: string) -> boolean
|
||||||
|
|
||||||
|
This function can check some path is a exist dir.
|
0
examples/README.md
Normal file
0
examples/README.md
Normal file
18
examples/plugin/init.lua
Normal file
18
examples/plugin/init.lua
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
local Api = require("./interface")
|
||||||
|
local log = Api.log;
|
||||||
|
|
||||||
|
local manager = {
|
||||||
|
name = "Dioxus-CLI Plugin Demo",
|
||||||
|
repository = "http://github.com/DioxusLabs/cli",
|
||||||
|
author = "YuKun Liu <mrxzx.info@gmail.com>",
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.onLoad = function ()
|
||||||
|
log.info("plugin loaded.")
|
||||||
|
end
|
||||||
|
|
||||||
|
manager.onStartBuild = function ()
|
||||||
|
log.warn("system start to build")
|
||||||
|
end
|
||||||
|
|
||||||
|
return manager
|
25
examples/plugin/interface.lua
Normal file
25
examples/plugin/interface.lua
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
local interface = {}
|
||||||
|
|
||||||
|
if plugin_logger ~= nil then
|
||||||
|
interface.log = plugin_logger
|
||||||
|
else
|
||||||
|
interface.log = {
|
||||||
|
trace = function (info)
|
||||||
|
print("trace: " .. info)
|
||||||
|
end,
|
||||||
|
debug = function (info)
|
||||||
|
print("debug: " .. info)
|
||||||
|
end,
|
||||||
|
info = function (info)
|
||||||
|
print("info: " .. info)
|
||||||
|
end,
|
||||||
|
warn = function (info)
|
||||||
|
print("warn: " .. info)
|
||||||
|
end,
|
||||||
|
error = function (info)
|
||||||
|
print("error: " .. info)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return interface
|
|
@ -40,25 +40,8 @@ script = []
|
||||||
# serve: [dev-server] only
|
# serve: [dev-server] only
|
||||||
script = []
|
script = []
|
||||||
|
|
||||||
[application.tools]
|
[application.plugins]
|
||||||
|
|
||||||
# use binaryen.wasm-opt for output Wasm file
|
available = true
|
||||||
# binaryen just will trigger in `web` platform
|
|
||||||
binaryen = { wasm_opt = true }
|
|
||||||
|
|
||||||
# use sass auto will auto check sass file and build it.
|
required = []
|
||||||
|
|
||||||
|
|
||||||
# [application.tools.sass]
|
|
||||||
|
|
||||||
# auto will check the assets dirs, and auto to transform all scss file to css file.
|
|
||||||
# input = "*"
|
|
||||||
|
|
||||||
# or you can specify some scss file -> css file
|
|
||||||
# input = [
|
|
||||||
# # some sass file path
|
|
||||||
# # this file will translate to `/css/test.css`
|
|
||||||
# "/css/test.scss"
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# source_map = true
|
|
|
@ -124,7 +124,7 @@ pub fn build(config: &CrateConfig, quiet: bool) -> Result<BuildResult> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
if bindgen_result.is_err() {
|
if bindgen_result.is_err() {
|
||||||
return Err(Error::BuildFailed("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.79` Bindgen crate.".to_string()));
|
return Err(Error::BuildFailed("Bindgen build failed! \nThis is probably due to the Bindgen version, dioxus-cli using `0.2.81` Bindgen crate.".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// check binaryen:wasm-opt tool
|
// check binaryen:wasm-opt tool
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::plugin::PluginManager;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Build the Rust WASM app and all of its assets.
|
/// Build the Rust WASM app and all of its assets.
|
||||||
|
@ -32,6 +34,8 @@ impl Build {
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let _ = PluginManager::on_build_start(&crate_config, &platform);
|
||||||
|
|
||||||
match platform.as_str() {
|
match platform.as_str() {
|
||||||
"web" => {
|
"web" => {
|
||||||
crate::builder::build(&crate_config, false)?;
|
crate::builder::build(&crate_config, false)?;
|
||||||
|
@ -61,6 +65,8 @@ impl Build {
|
||||||
)?;
|
)?;
|
||||||
file.write_all(temp.as_bytes())?;
|
file.write_all(temp.as_bytes())?;
|
||||||
|
|
||||||
|
let _ = PluginManager::on_build_finish(&crate_config, &platform);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub mod clean;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod create;
|
pub mod create;
|
||||||
pub mod serve;
|
pub mod serve;
|
||||||
pub mod tool;
|
pub mod plugin;
|
||||||
pub mod translate;
|
pub mod translate;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -52,7 +52,21 @@ pub enum Commands {
|
||||||
/// Dioxus config file controls.
|
/// Dioxus config file controls.
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Config(config::Config),
|
Config(config::Config),
|
||||||
/// Install & Manage tools for Dioxus-cli.
|
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Tool(tool::Tool),
|
/// Manage plugins for dioxus cli
|
||||||
|
Plugin(plugin::Plugin),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Commands {
|
||||||
|
pub fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Commands::Build(_) => "build",
|
||||||
|
Commands::Translate(_) => "translate",
|
||||||
|
Commands::Serve(_) => "sevre",
|
||||||
|
Commands::Create(_) => "create",
|
||||||
|
Commands::Clean(_) => "clean",
|
||||||
|
Commands::Config(_) => "config",
|
||||||
|
Commands::Plugin(_) => "plugin",
|
||||||
|
}.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
36
src/cli/plugin/mod.rs
Normal file
36
src/cli/plugin/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Build the Rust WASM app and all of its assets.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Subcommand)]
|
||||||
|
#[clap(name = "plugin")]
|
||||||
|
pub enum Plugin {
|
||||||
|
/// Return all dioxus-cli support tools.
|
||||||
|
List {},
|
||||||
|
/// Get default app install path.
|
||||||
|
AppPath {},
|
||||||
|
/// Install a new tool.
|
||||||
|
Add { name: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin {
|
||||||
|
pub async fn plugin(self) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Plugin::List {} => {
|
||||||
|
for item in crate::plugin::PluginManager::plugin_list() {
|
||||||
|
println!("- {item}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Plugin::AppPath {} => {
|
||||||
|
if let Some(v) = crate::plugin::PluginManager::init_plugin_dir().to_str() {
|
||||||
|
println!("{}", v);
|
||||||
|
} else {
|
||||||
|
log::error!("Plugin path get failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Plugin::Add { name: _ } => {
|
||||||
|
log::info!("You can use `dioxus plugin app-path` to get Installation position");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,13 @@ use std::{collections::HashMap, fs::File, io::Read, path::PathBuf};
|
||||||
pub struct DioxusConfig {
|
pub struct DioxusConfig {
|
||||||
pub application: ApplicationConfig,
|
pub application: ApplicationConfig,
|
||||||
pub web: WebConfig,
|
pub web: WebConfig,
|
||||||
|
|
||||||
|
#[serde(default = "default_plugin")]
|
||||||
|
pub plugin: toml::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_plugin() -> toml::Value {
|
||||||
|
toml::Value::Boolean(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DioxusConfig {
|
impl DioxusConfig {
|
||||||
|
@ -22,7 +29,7 @@ impl DioxusConfig {
|
||||||
dioxus_conf_file.read_to_string(&mut meta_str)?;
|
dioxus_conf_file.read_to_string(&mut meta_str)?;
|
||||||
|
|
||||||
toml::from_str::<DioxusConfig>(&meta_str)
|
toml::from_str::<DioxusConfig>(&meta_str)
|
||||||
.map_err(|_| crate::Error::Unique("Dioxus.toml parse failed".into()))
|
.map_err(|_| crate::Error::Unique("Dioxus.toml parse failed".into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +41,9 @@ impl Default for DioxusConfig {
|
||||||
default_platform: "web".to_string(),
|
default_platform: "web".to_string(),
|
||||||
out_dir: Some(PathBuf::from("dist")),
|
out_dir: Some(PathBuf::from("dist")),
|
||||||
asset_dir: Some(PathBuf::from("public")),
|
asset_dir: Some(PathBuf::from("public")),
|
||||||
|
|
||||||
tools: None,
|
tools: None,
|
||||||
|
|
||||||
sub_package: None,
|
sub_package: None,
|
||||||
},
|
},
|
||||||
web: WebConfig {
|
web: WebConfig {
|
||||||
|
@ -56,6 +65,7 @@ impl Default for DioxusConfig {
|
||||||
script: Some(vec![]),
|
script: Some(vec![]),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
plugin: toml::Value::Table(toml::map::Map::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +76,9 @@ pub struct ApplicationConfig {
|
||||||
pub default_platform: String,
|
pub default_platform: String,
|
||||||
pub out_dir: Option<PathBuf>,
|
pub out_dir: Option<PathBuf>,
|
||||||
pub asset_dir: Option<PathBuf>,
|
pub asset_dir: Option<PathBuf>,
|
||||||
|
|
||||||
pub tools: Option<HashMap<String, toml::Value>>,
|
pub tools: Option<HashMap<String, toml::Value>>,
|
||||||
|
|
||||||
pub sub_package: Option<String>,
|
pub sub_package: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,20 +229,4 @@ impl CrateConfig {
|
||||||
self.features = Some(features);
|
self.features = Some(features);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn with_build_options(&mut self, options: &BuildOptions) {
|
|
||||||
// if let Some(name) = &options.example {
|
|
||||||
// self.as_example(name.clone());
|
|
||||||
// }
|
|
||||||
// self.release = options.release;
|
|
||||||
// self.out_dir = options.outdir.clone().into();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pub fn with_develop_options(&mut self, options: &DevelopOptions) {
|
|
||||||
// if let Some(name) = &options.example {
|
|
||||||
// self.as_example(name.clone());
|
|
||||||
// }
|
|
||||||
// self.release = options.release;
|
|
||||||
// self.out_dir = tempfile::Builder::new().tempdir().expect("").into_path();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,3 +22,4 @@ pub mod logging;
|
||||||
pub use logging::*;
|
pub use logging::*;
|
||||||
|
|
||||||
pub mod hot_reload;
|
pub mod hot_reload;
|
||||||
|
pub mod plugin;
|
16
src/main.rs
16
src/main.rs
|
@ -1,5 +1,5 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use dioxus_cli::*;
|
use dioxus_cli::{plugin::PluginManager, *};
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -7,6 +7,15 @@ async fn main() -> Result<()> {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
set_up_logging();
|
set_up_logging();
|
||||||
|
|
||||||
|
let dioxus_config = DioxusConfig::load()?;
|
||||||
|
|
||||||
|
let plugin_state = PluginManager::init(dioxus_config.plugin);
|
||||||
|
|
||||||
|
if let Err(e) = plugin_state {
|
||||||
|
log::error!("🚫 Plugin system initialization failed: {e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
match args.action {
|
match args.action {
|
||||||
Commands::Translate(opts) => {
|
Commands::Translate(opts) => {
|
||||||
if let Err(e) = opts.translate() {
|
if let Err(e) = opts.translate() {
|
||||||
|
@ -50,10 +59,9 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Tool(opts) => {
|
Commands::Plugin(opts) => {
|
||||||
if let Err(e) = opts.tool().await {
|
if let Err(e) = opts.plugin().await {
|
||||||
log::error!("tool error: {}", e);
|
log::error!("tool error: {}", e);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
65
src/plugin/interface/command.rs
Normal file
65
src/plugin/interface/command.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
use mlua::{FromLua, UserData};
|
||||||
|
|
||||||
|
enum StdioFromString {
|
||||||
|
Inhert,
|
||||||
|
Piped,
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
impl<'lua> FromLua<'lua> for StdioFromString {
|
||||||
|
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
if let mlua::Value::String(v) = lua_value {
|
||||||
|
let v = v.to_str().unwrap();
|
||||||
|
return Ok(match v.to_lowercase().as_str() {
|
||||||
|
"inhert" => Self::Inhert,
|
||||||
|
"piped" => Self::Piped,
|
||||||
|
"null" => Self::Null,
|
||||||
|
_ => Self::Inhert,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(Self::Inhert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl StdioFromString {
|
||||||
|
pub fn to_stdio(self) -> Stdio {
|
||||||
|
match self {
|
||||||
|
StdioFromString::Inhert => Stdio::inherit(),
|
||||||
|
StdioFromString::Piped => Stdio::piped(),
|
||||||
|
StdioFromString::Null => Stdio::null(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PluginCommander;
|
||||||
|
impl UserData for PluginCommander {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function(
|
||||||
|
"exec",
|
||||||
|
|_, args: (Vec<String>, StdioFromString, StdioFromString)| {
|
||||||
|
|
||||||
|
let cmd = args.0;
|
||||||
|
let stdout = args.1;
|
||||||
|
let stderr = args.2;
|
||||||
|
|
||||||
|
if cmd.len() == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let cmd_name = cmd.get(0).unwrap();
|
||||||
|
let mut command = Command::new(cmd_name);
|
||||||
|
let t = cmd
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, _)| *i > 0)
|
||||||
|
.map(|v| v.1.clone())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
command.args(t);
|
||||||
|
command.stdout(stdout.to_stdio()).stderr(stderr.to_stdio());
|
||||||
|
command.output()?;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(_fields: &mut F) {}
|
||||||
|
}
|
13
src/plugin/interface/dirs.rs
Normal file
13
src/plugin/interface/dirs.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use mlua::UserData;
|
||||||
|
|
||||||
|
use crate::tools::app_path;
|
||||||
|
|
||||||
|
pub struct PluginDirs;
|
||||||
|
impl UserData for PluginDirs {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("plugins_dir", |_, ()| {
|
||||||
|
let path = app_path().join("plugins");
|
||||||
|
Ok(path.to_str().unwrap().to_string())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
87
src/plugin/interface/fs.rs
Normal file
87
src/plugin/interface/fs.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use std::{
|
||||||
|
fs::{create_dir, create_dir_all, remove_dir_all},
|
||||||
|
path::PathBuf, io::{Read, Write},
|
||||||
|
};
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use mlua::UserData;
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
|
use tar::Archive;
|
||||||
|
use crate::tools::extract_zip;
|
||||||
|
|
||||||
|
pub struct PluginFileSystem;
|
||||||
|
impl UserData for PluginFileSystem {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("create_dir", |_, args: (String, bool)| {
|
||||||
|
let path = args.0;
|
||||||
|
let recursive = args.1;
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
if !path.exists() {
|
||||||
|
let v = if recursive {
|
||||||
|
create_dir_all(path)
|
||||||
|
} else {
|
||||||
|
create_dir(path)
|
||||||
|
};
|
||||||
|
return Ok(v.is_ok());
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
});
|
||||||
|
methods.add_function("remove_dir", |_, path: String| {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
let r = remove_dir_all(path);
|
||||||
|
Ok(r.is_ok())
|
||||||
|
});
|
||||||
|
methods.add_function("file_get_content", |_, path: String| {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
let mut file = std::fs::File::open(path)?;
|
||||||
|
let mut buffer = String::new();
|
||||||
|
file.read_to_string(&mut buffer)?;
|
||||||
|
Ok(buffer)
|
||||||
|
});
|
||||||
|
methods.add_function("file_set_content", |_, args: (String, String)| {
|
||||||
|
let path = args.0;
|
||||||
|
let content = args.1;
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
|
||||||
|
let file = std::fs::File::create(path);
|
||||||
|
if file.is_err() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if file.unwrap().write_all(content.as_bytes()).is_err() {
|
||||||
|
return Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
});
|
||||||
|
methods.add_function("unzip_file", |_, args: (String, String)| {
|
||||||
|
let file = PathBuf::from(args.0);
|
||||||
|
let target = PathBuf::from(args.1);
|
||||||
|
let res = extract_zip(&file, &target);
|
||||||
|
if let Err(_) = res {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
});
|
||||||
|
methods.add_function("untar_gz_file", |_, args: (String, String)| {
|
||||||
|
|
||||||
|
let file = PathBuf::from(args.0);
|
||||||
|
let target = PathBuf::from(args.1);
|
||||||
|
|
||||||
|
let tar_gz = if let Ok(v) = File::open(file) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
return Ok(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
let tar = GzDecoder::new(tar_gz);
|
||||||
|
let mut archive = Archive::new(tar);
|
||||||
|
if archive.unpack(&target).is_err() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
28
src/plugin/interface/log.rs
Normal file
28
src/plugin/interface/log.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
use log;
|
||||||
|
use mlua::UserData;
|
||||||
|
|
||||||
|
pub struct PluginLogger;
|
||||||
|
impl UserData for PluginLogger {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("trace", |_, info: String| {
|
||||||
|
log::trace!("{}", info);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_function("info", |_, info: String| {
|
||||||
|
log::info!("{}", info);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_function("debug", |_, info: String| {
|
||||||
|
log::debug!("{}", info);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_function("warn", |_, info: String| {
|
||||||
|
log::warn!("{}", info);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_function("error", |_, info: String| {
|
||||||
|
log::error!("{}", info);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
233
src/plugin/interface/mod.rs
Normal file
233
src/plugin/interface/mod.rs
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
use mlua::{FromLua, Function, ToLua};
|
||||||
|
|
||||||
|
pub mod command;
|
||||||
|
pub mod dirs;
|
||||||
|
pub mod fs;
|
||||||
|
pub mod log;
|
||||||
|
pub mod network;
|
||||||
|
pub mod os;
|
||||||
|
pub mod path;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PluginInfo<'lua> {
|
||||||
|
pub name: String,
|
||||||
|
pub repository: String,
|
||||||
|
pub author: String,
|
||||||
|
pub version: String,
|
||||||
|
|
||||||
|
pub inner: PluginInner,
|
||||||
|
|
||||||
|
pub on_init: Option<Function<'lua>>,
|
||||||
|
pub build: PluginBuildInfo<'lua>,
|
||||||
|
pub serve: PluginServeInfo<'lua>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for PluginInfo<'lua> {
|
||||||
|
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
let mut res = Self {
|
||||||
|
name: String::default(),
|
||||||
|
repository: String::default(),
|
||||||
|
author: String::default(),
|
||||||
|
version: String::from("0.1.0"),
|
||||||
|
|
||||||
|
inner: Default::default(),
|
||||||
|
|
||||||
|
on_init: None,
|
||||||
|
build: Default::default(),
|
||||||
|
serve: Default::default(),
|
||||||
|
};
|
||||||
|
if let mlua::Value::Table(tab) = lua_value {
|
||||||
|
if let Ok(v) = tab.get::<_, String>("name") {
|
||||||
|
res.name = v;
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, String>("repository") {
|
||||||
|
res.repository = v;
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, String>("author") {
|
||||||
|
res.author = v;
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, String>("version") {
|
||||||
|
res.version = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(v) = tab.get::<_, PluginInner>("inner") {
|
||||||
|
res.inner = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(v) = tab.get::<_, Function>("on_init") {
|
||||||
|
res.on_init = Some(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(v) = tab.get::<_, PluginBuildInfo>("build") {
|
||||||
|
res.build = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(v) = tab.get::<_, PluginServeInfo>("serve") {
|
||||||
|
res.serve = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for PluginInfo<'lua> {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
let res = lua.create_table()?;
|
||||||
|
|
||||||
|
res.set("name", self.name.to_string())?;
|
||||||
|
res.set("repository", self.repository.to_string())?;
|
||||||
|
res.set("author", self.author.to_string())?;
|
||||||
|
res.set("version", self.version.to_string())?;
|
||||||
|
|
||||||
|
res.set("inner", self.inner)?;
|
||||||
|
|
||||||
|
if let Some(e) = self.on_init {
|
||||||
|
res.set("on_init", e)?;
|
||||||
|
}
|
||||||
|
res.set("build", self.build)?;
|
||||||
|
res.set("serve", self.serve)?;
|
||||||
|
|
||||||
|
Ok(mlua::Value::Table(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct PluginInner {
|
||||||
|
pub plugin_dir: String,
|
||||||
|
pub from_loader: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for PluginInner {
|
||||||
|
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
let mut res = Self {
|
||||||
|
plugin_dir: String::new(),
|
||||||
|
from_loader: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let mlua::Value::Table(t) = lua_value {
|
||||||
|
if let Ok(v) = t.get::<_, String>("plugin_dir") {
|
||||||
|
res.plugin_dir = v;
|
||||||
|
}
|
||||||
|
if let Ok(v) = t.get::<_, bool>("from_loader") {
|
||||||
|
res.from_loader = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for PluginInner {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
let res = lua.create_table()?;
|
||||||
|
|
||||||
|
res.set("plugin_dir", self.plugin_dir)?;
|
||||||
|
res.set("from_loader", self.from_loader)?;
|
||||||
|
|
||||||
|
Ok(mlua::Value::Table(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct PluginBuildInfo<'lua> {
|
||||||
|
pub on_start: Option<Function<'lua>>,
|
||||||
|
pub on_finish: Option<Function<'lua>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for PluginBuildInfo<'lua> {
|
||||||
|
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
let mut res = Self {
|
||||||
|
on_start: None,
|
||||||
|
on_finish: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let mlua::Value::Table(t) = lua_value {
|
||||||
|
if let Ok(v) = t.get::<_, Function>("on_start") {
|
||||||
|
res.on_start = Some(v);
|
||||||
|
}
|
||||||
|
if let Ok(v) = t.get::<_, Function>("on_finish") {
|
||||||
|
res.on_finish = Some(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for PluginBuildInfo<'lua> {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
let res = lua.create_table()?;
|
||||||
|
|
||||||
|
if let Some(v) = self.on_start {
|
||||||
|
res.set("on_start", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.on_finish {
|
||||||
|
res.set("on_finish", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mlua::Value::Table(res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct PluginServeInfo<'lua> {
|
||||||
|
pub interval: i32,
|
||||||
|
|
||||||
|
pub on_start: Option<Function<'lua>>,
|
||||||
|
pub on_interval: Option<Function<'lua>>,
|
||||||
|
pub on_rebuild: Option<Function<'lua>>,
|
||||||
|
pub on_shutdown: Option<Function<'lua>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for PluginServeInfo<'lua> {
|
||||||
|
fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
let mut res = Self::default();
|
||||||
|
|
||||||
|
if let mlua::Value::Table(tab) = lua_value {
|
||||||
|
if let Ok(v) = tab.get::<_, i32>("interval") {
|
||||||
|
res.interval = v;
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, Function>("on_start") {
|
||||||
|
res.on_start = Some(v);
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, Function>("on_interval") {
|
||||||
|
res.on_interval = Some(v);
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, Function>("on_rebuild") {
|
||||||
|
res.on_rebuild = Some(v);
|
||||||
|
}
|
||||||
|
if let Ok(v) = tab.get::<_, Function>("on_shutdown") {
|
||||||
|
res.on_shutdown = Some(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for PluginServeInfo<'lua> {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
let res = lua.create_table()?;
|
||||||
|
|
||||||
|
res.set("interval", self.interval)?;
|
||||||
|
|
||||||
|
if let Some(v) = self.on_start {
|
||||||
|
res.set("on_start", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.on_interval {
|
||||||
|
res.set("on_interval", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.on_rebuild {
|
||||||
|
res.set("on_rebuild", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(v) = self.on_shutdown {
|
||||||
|
res.set("on_shutdown", v)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(mlua::Value::Table(res))
|
||||||
|
}
|
||||||
|
}
|
27
src/plugin/interface/network.rs
Normal file
27
src/plugin/interface/network.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use std::{io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
|
use mlua::UserData;
|
||||||
|
|
||||||
|
pub struct PluginNetwork;
|
||||||
|
impl UserData for PluginNetwork {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("download_file", |_, args: (String, String)| {
|
||||||
|
let url = args.0;
|
||||||
|
let path = args.1;
|
||||||
|
|
||||||
|
let resp = reqwest::blocking::get(url);
|
||||||
|
if let Ok(resp) = resp {
|
||||||
|
let mut content = Cursor::new(resp.bytes().unwrap());
|
||||||
|
let file = std::fs::File::create(PathBuf::from(path));
|
||||||
|
if file.is_err() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
let mut file = file.unwrap();
|
||||||
|
let res = std::io::copy(&mut content, &mut file);
|
||||||
|
return Ok(res.is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
18
src/plugin/interface/os.rs
Normal file
18
src/plugin/interface/os.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use mlua::UserData;
|
||||||
|
|
||||||
|
pub struct PluginOS;
|
||||||
|
impl UserData for PluginOS {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("current_platform", |_, ()| {
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
Ok("windows")
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
Ok("macos")
|
||||||
|
} else if cfg!(target_os = "linux") {
|
||||||
|
Ok("linux")
|
||||||
|
} else {
|
||||||
|
panic!("unsupported platformm");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
40
src/plugin/interface/path.rs
Normal file
40
src/plugin/interface/path.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use mlua::{UserData, Variadic};
|
||||||
|
|
||||||
|
pub struct PluginPath;
|
||||||
|
impl UserData for PluginPath {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
// join function
|
||||||
|
methods.add_function("join", |_, args: Variadic<String>| {
|
||||||
|
let mut path = PathBuf::new();
|
||||||
|
for i in args {
|
||||||
|
path = path.join(i);
|
||||||
|
}
|
||||||
|
Ok(path.to_str().unwrap().to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
// parent function
|
||||||
|
methods.add_function("parent", |_, path: String| {
|
||||||
|
let current_path = PathBuf::from(&path);
|
||||||
|
let parent = current_path.parent();
|
||||||
|
if parent.is_none() {
|
||||||
|
return Ok(path);
|
||||||
|
} else {
|
||||||
|
return Ok(parent.unwrap().to_str().unwrap().to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
methods.add_function("exists", |_, path: String| {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
Ok(path.exists())
|
||||||
|
});
|
||||||
|
methods.add_function("is_dir", |_, path: String| {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
Ok(path.is_dir())
|
||||||
|
});
|
||||||
|
methods.add_function("is_file", |_, path: String| {
|
||||||
|
let path = PathBuf::from(path);
|
||||||
|
Ok(path.is_file())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
330
src/plugin/mod.rs
Normal file
330
src/plugin/mod.rs
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
tools::{app_path, clone_repo},
|
||||||
|
CrateConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
interface::PluginInfo,
|
||||||
|
interface::{
|
||||||
|
command::PluginCommander, dirs::PluginDirs, fs::PluginFileSystem, log::PluginLogger,
|
||||||
|
network::PluginNetwork, os::PluginOS, path::PluginPath,
|
||||||
|
},
|
||||||
|
types::PluginConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod interface;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref LUA: Mutex<Lua> = Mutex::new(Lua::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PluginManager;
|
||||||
|
|
||||||
|
impl PluginManager {
|
||||||
|
pub fn init(config: toml::Value) -> anyhow::Result<()> {
|
||||||
|
let config = PluginConfig::from_toml_value(config);
|
||||||
|
|
||||||
|
if !config.available {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
let manager = lua.create_table().unwrap();
|
||||||
|
let name_index = lua.create_table().unwrap();
|
||||||
|
|
||||||
|
let plugin_dir = Self::init_plugin_dir();
|
||||||
|
|
||||||
|
let api = lua.create_table().unwrap();
|
||||||
|
|
||||||
|
api.set("log", PluginLogger).unwrap();
|
||||||
|
api.set("command", PluginCommander).unwrap();
|
||||||
|
api.set("network", PluginNetwork).unwrap();
|
||||||
|
api.set("dirs", PluginDirs).unwrap();
|
||||||
|
api.set("fs", PluginFileSystem).unwrap();
|
||||||
|
api.set("path", PluginPath).unwrap();
|
||||||
|
api.set("os", PluginOS).unwrap();
|
||||||
|
|
||||||
|
lua.globals().set("plugin_lib", api).unwrap();
|
||||||
|
lua.globals()
|
||||||
|
.set("library_dir", plugin_dir.to_str().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
lua.globals().set("config_info", config.clone())?;
|
||||||
|
|
||||||
|
let mut index: u32 = 1;
|
||||||
|
let dirs = std::fs::read_dir(&plugin_dir)?;
|
||||||
|
|
||||||
|
let mut path_list = dirs
|
||||||
|
.filter(|v| v.is_ok())
|
||||||
|
.map(|v| (v.unwrap().path(), false))
|
||||||
|
.collect::<Vec<(PathBuf, bool)>>();
|
||||||
|
for i in &config.loader {
|
||||||
|
let path = PathBuf::from(i);
|
||||||
|
if !path.is_dir() {
|
||||||
|
// for loader dir, we need check first, because we need give a error log.
|
||||||
|
log::error!("Plugin loader: {:?} path is not a exists directory.", path);
|
||||||
|
}
|
||||||
|
path_list.push((path, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in path_list {
|
||||||
|
let plugin_dir = entry.0.to_path_buf();
|
||||||
|
|
||||||
|
if plugin_dir.is_dir() {
|
||||||
|
let init_file = plugin_dir.join("init.lua");
|
||||||
|
if init_file.is_file() {
|
||||||
|
let mut file = std::fs::File::open(init_file).unwrap();
|
||||||
|
let mut buffer = String::new();
|
||||||
|
file.read_to_string(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
let current_plugin_dir = plugin_dir.to_str().unwrap().to_string();
|
||||||
|
let from_loader = entry.1;
|
||||||
|
|
||||||
|
lua.globals()
|
||||||
|
.set("_temp_plugin_dir", current_plugin_dir.clone())?;
|
||||||
|
lua.globals().set("_temp_from_loader", from_loader)?;
|
||||||
|
|
||||||
|
let info = lua.load(&buffer).eval::<PluginInfo>();
|
||||||
|
match info {
|
||||||
|
Ok(mut info) => {
|
||||||
|
if name_index.contains_key(info.name.clone()).unwrap_or(false)
|
||||||
|
&& !from_loader
|
||||||
|
{
|
||||||
|
// found same name plugin, intercept load
|
||||||
|
log::warn!(
|
||||||
|
"Plugin {} has been intercepted. [mulit-load]",
|
||||||
|
info.name
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info.inner.plugin_dir = current_plugin_dir;
|
||||||
|
info.inner.from_loader = from_loader;
|
||||||
|
|
||||||
|
// call `on_init` if file "dcp.json" not exists
|
||||||
|
let dcp_file = plugin_dir.join("dcp.json");
|
||||||
|
if !dcp_file.is_file() {
|
||||||
|
if let Some(func) = info.clone().on_init {
|
||||||
|
let result = func.call::<_, bool>(());
|
||||||
|
match result {
|
||||||
|
Ok(true) => {
|
||||||
|
// plugin init success, create `dcp.json` file.
|
||||||
|
let mut file = std::fs::File::create(dcp_file).unwrap();
|
||||||
|
let value = json!({
|
||||||
|
"name": info.name,
|
||||||
|
"author": info.author,
|
||||||
|
"repository": info.repository,
|
||||||
|
"version": info.version,
|
||||||
|
"generate_time": chrono::Local::now().timestamp(),
|
||||||
|
});
|
||||||
|
let buffer =
|
||||||
|
serde_json::to_string_pretty(&value).unwrap();
|
||||||
|
let buffer = buffer.as_bytes();
|
||||||
|
file.write_all(buffer).unwrap();
|
||||||
|
|
||||||
|
// insert plugin-info into plugin-manager
|
||||||
|
if let Ok(index) =
|
||||||
|
name_index.get::<_, u32>(info.name.clone())
|
||||||
|
{
|
||||||
|
let _ = manager.set(index, info.clone());
|
||||||
|
} else {
|
||||||
|
let _ = manager.set(index, info.clone());
|
||||||
|
index += 1;
|
||||||
|
let _ = name_index.set(info.name, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false) => {
|
||||||
|
log::warn!("Plugin init function result is `false`, init failed.");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Plugin init failed: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(index) = name_index.get::<_, u32>(info.name.clone()) {
|
||||||
|
let _ = manager.set(index, info.clone());
|
||||||
|
} else {
|
||||||
|
let _ = manager.set(index, info.clone());
|
||||||
|
index += 1;
|
||||||
|
let _ = name_index.set(info.name, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
let dir_name = plugin_dir.file_name().unwrap().to_str().unwrap();
|
||||||
|
log::error!("Plugin '{dir_name}' load failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lua.globals().set("manager", manager).unwrap();
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_build_start(crate_config: &CrateConfig, platform: &str) -> anyhow::Result<()> {
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
if !lua.globals().contains_key("manager")? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
||||||
|
|
||||||
|
let args = lua.create_table()?;
|
||||||
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
||||||
|
args.set("platform", platform)?;
|
||||||
|
args.set("out_dir", crate_config.out_dir.to_str().unwrap())?;
|
||||||
|
args.set("asset_dir", crate_config.asset_dir.to_str().unwrap())?;
|
||||||
|
|
||||||
|
for i in 1..(manager.len()? as i32 + 1) {
|
||||||
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
||||||
|
if let Some(func) = info.build.on_start {
|
||||||
|
func.call::<Table, ()>(args.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_build_finish(crate_config: &CrateConfig, platform: &str) -> anyhow::Result<()> {
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
if !lua.globals().contains_key("manager")? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
||||||
|
|
||||||
|
let args = lua.create_table()?;
|
||||||
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
||||||
|
args.set("platform", platform)?;
|
||||||
|
args.set("out_dir", crate_config.out_dir.to_str().unwrap())?;
|
||||||
|
args.set("asset_dir", crate_config.asset_dir.to_str().unwrap())?;
|
||||||
|
|
||||||
|
for i in 1..(manager.len()? as i32 + 1) {
|
||||||
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
||||||
|
if let Some(func) = info.build.on_finish {
|
||||||
|
func.call::<Table, ()>(args.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_serve_start(crate_config: &CrateConfig) -> anyhow::Result<()> {
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
if !lua.globals().contains_key("manager")? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
||||||
|
|
||||||
|
let args = lua.create_table()?;
|
||||||
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
||||||
|
|
||||||
|
for i in 1..(manager.len()? as i32 + 1) {
|
||||||
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
||||||
|
if let Some(func) = info.serve.on_start {
|
||||||
|
func.call::<Table, ()>(args.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_serve_rebuild(timestamp: i64, files: Vec<PathBuf>) -> anyhow::Result<()> {
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
||||||
|
|
||||||
|
let args = lua.create_table()?;
|
||||||
|
args.set("timestamp", timestamp)?;
|
||||||
|
let files: Vec<String> = files
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.to_str().unwrap().to_string())
|
||||||
|
.collect();
|
||||||
|
args.set("changed_files", files)?;
|
||||||
|
|
||||||
|
for i in 1..(manager.len()? as i32 + 1) {
|
||||||
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
||||||
|
if let Some(func) = info.serve.on_rebuild {
|
||||||
|
func.call::<Table, ()>(args.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_serve_shutdown(crate_config: &CrateConfig) -> anyhow::Result<()> {
|
||||||
|
let lua = LUA.lock().unwrap();
|
||||||
|
|
||||||
|
if !lua.globals().contains_key("manager")? {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let manager = lua.globals().get::<_, Table>("manager")?;
|
||||||
|
|
||||||
|
let args = lua.create_table()?;
|
||||||
|
args.set("name", crate_config.dioxus_config.application.name.clone())?;
|
||||||
|
|
||||||
|
for i in 1..(manager.len()? as i32 + 1) {
|
||||||
|
let info = manager.get::<i32, PluginInfo>(i)?;
|
||||||
|
if let Some(func) = info.serve.on_shutdown {
|
||||||
|
func.call::<Table, ()>(args.clone())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_plugin_dir() -> PathBuf {
|
||||||
|
let app_path = app_path();
|
||||||
|
let plugin_path = app_path.join("plugins");
|
||||||
|
if !plugin_path.is_dir() {
|
||||||
|
log::info!("📖 Start to init plugin library ...");
|
||||||
|
let url = "https://github.com/DioxusLabs/cli-plugin-library";
|
||||||
|
clone_repo(&plugin_path, url).unwrap();
|
||||||
|
}
|
||||||
|
plugin_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plugin_list() -> Vec<String> {
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
if let Ok(lua) = LUA.lock() {
|
||||||
|
let list = lua
|
||||||
|
.load(mlua::chunk!(
|
||||||
|
local list = {}
|
||||||
|
for key, value in ipairs(manager) do
|
||||||
|
table.insert(list, {name = value.name, loader = value.inner.from_loader})
|
||||||
|
end
|
||||||
|
return list
|
||||||
|
))
|
||||||
|
.eval::<Vec<Table>>()
|
||||||
|
.unwrap_or_default();
|
||||||
|
for i in list {
|
||||||
|
let name = i.get::<_, String>("name").unwrap();
|
||||||
|
let loader = i.get::<_, bool>("loader").unwrap();
|
||||||
|
|
||||||
|
let text = if loader {
|
||||||
|
format!("{name} [:loader]")
|
||||||
|
} else {
|
||||||
|
name
|
||||||
|
};
|
||||||
|
res.push(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
138
src/plugin/types.rs
Normal file
138
src/plugin/types.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use mlua::ToLua;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PluginConfig {
|
||||||
|
pub available: bool,
|
||||||
|
pub loader: Vec<String>,
|
||||||
|
pub config_info: HashMap<String, HashMap<String, Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for PluginConfig {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
let table = lua.create_table()?;
|
||||||
|
|
||||||
|
table.set("available", self.available)?;
|
||||||
|
table.set("loader", self.loader)?;
|
||||||
|
|
||||||
|
let config_info = lua.create_table()?;
|
||||||
|
|
||||||
|
for (name, data) in self.config_info {
|
||||||
|
config_info.set(name, data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.set("config_info", config_info)?;
|
||||||
|
|
||||||
|
Ok(mlua::Value::Table(table))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginConfig {
|
||||||
|
pub fn from_toml_value(val: toml::Value) -> Self {
|
||||||
|
if let toml::Value::Table(tab) = val {
|
||||||
|
let available = tab
|
||||||
|
.get::<_>("available")
|
||||||
|
.unwrap_or(&toml::Value::Boolean(true));
|
||||||
|
let available = available.as_bool().unwrap_or(true);
|
||||||
|
|
||||||
|
let mut loader = vec![];
|
||||||
|
if let Some(origin) = tab.get("loader") {
|
||||||
|
if origin.is_array() {
|
||||||
|
for i in origin.as_array().unwrap() {
|
||||||
|
loader.push(i.as_str().unwrap_or_default().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut config_info = HashMap::new();
|
||||||
|
|
||||||
|
for (name, value) in tab {
|
||||||
|
if name == "available" || name == "loader" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let toml::Value::Table(value) = value {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
for (item, info) in value {
|
||||||
|
map.insert(item, Value::from_toml(info));
|
||||||
|
}
|
||||||
|
config_info.insert(name, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
available,
|
||||||
|
loader,
|
||||||
|
config_info,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Self {
|
||||||
|
available: false,
|
||||||
|
loader: vec![],
|
||||||
|
config_info: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Value {
|
||||||
|
String(String),
|
||||||
|
Integer(i64),
|
||||||
|
Float(f64),
|
||||||
|
Boolean(bool),
|
||||||
|
Array(Vec<Value>),
|
||||||
|
Table(HashMap<String, Value>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn from_toml(origin: toml::Value) -> Self {
|
||||||
|
match origin {
|
||||||
|
cargo_toml::Value::String(s) => Value::String(s),
|
||||||
|
cargo_toml::Value::Integer(i) => Value::Integer(i),
|
||||||
|
cargo_toml::Value::Float(f) => Value::Float(f),
|
||||||
|
cargo_toml::Value::Boolean(b) => Value::Boolean(b),
|
||||||
|
cargo_toml::Value::Datetime(d) => Value::String(d.to_string()),
|
||||||
|
cargo_toml::Value::Array(a) => {
|
||||||
|
let mut v = vec![];
|
||||||
|
for i in a {
|
||||||
|
v.push(Value::from_toml(i));
|
||||||
|
}
|
||||||
|
Value::Array(v)
|
||||||
|
}
|
||||||
|
cargo_toml::Value::Table(t) => {
|
||||||
|
let mut h = HashMap::new();
|
||||||
|
for (n, v) in t {
|
||||||
|
h.insert(n, Value::from_toml(v));
|
||||||
|
}
|
||||||
|
Value::Table(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for Value {
|
||||||
|
fn to_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||||
|
Ok(match self {
|
||||||
|
Value::String(s) => mlua::Value::String(lua.create_string(&s)?),
|
||||||
|
Value::Integer(i) => mlua::Value::Integer(i),
|
||||||
|
Value::Float(f) => mlua::Value::Number(f),
|
||||||
|
Value::Boolean(b) => mlua::Value::Boolean(b),
|
||||||
|
Value::Array(a) => {
|
||||||
|
let table = lua.create_table()?;
|
||||||
|
for (i, v) in a.iter().enumerate() {
|
||||||
|
table.set(i, v.clone())?;
|
||||||
|
}
|
||||||
|
mlua::Value::Table(table)
|
||||||
|
}
|
||||||
|
Value::Table(t) => {
|
||||||
|
let table = lua.create_table()?;
|
||||||
|
for (i, v) in t.iter() {
|
||||||
|
table.set(i.clone(), v.clone())?;
|
||||||
|
}
|
||||||
|
mlua::Value::Table(table)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ use std::{net::UdpSocket, path::PathBuf, process::Command, sync::Arc};
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody};
|
use tower_http::services::fs::{ServeDir, ServeFileSystemResponseBody};
|
||||||
|
|
||||||
use crate::{builder, serve::Serve, BuildResult, CrateConfig, Result};
|
use crate::{builder, plugin::PluginManager, serve::Serve, BuildResult, CrateConfig, Result};
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
|
|
||||||
mod hot_reload;
|
mod hot_reload;
|
||||||
|
@ -54,6 +54,13 @@ struct WsReloadState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn startup(port: u16, config: CrateConfig) -> Result<()> {
|
pub async fn startup(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
|
// ctrl-c shutdown checker
|
||||||
|
let crate_config = config.clone();
|
||||||
|
let _ = ctrlc::set_handler(move || {
|
||||||
|
let _ = PluginManager::on_serve_shutdown(&crate_config);
|
||||||
|
std::process::exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
if config.hot_reload {
|
if config.hot_reload {
|
||||||
startup_hot_reload(port, config).await?
|
startup_hot_reload(port, config).await?
|
||||||
} else {
|
} else {
|
||||||
|
@ -62,11 +69,14 @@ pub async fn startup(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_assignments)]
|
||||||
pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
let first_build_result = crate::builder::build(&config, false)?;
|
let first_build_result = crate::builder::build(&config, false)?;
|
||||||
|
|
||||||
log::info!("🚀 Starting development server...");
|
log::info!("🚀 Starting development server...");
|
||||||
|
|
||||||
|
PluginManager::on_serve_start(&config)?;
|
||||||
|
|
||||||
let dist_path = config.out_dir.clone();
|
let dist_path = config.out_dir.clone();
|
||||||
let (reload_tx, _) = broadcast::channel(100);
|
let (reload_tx, _) = broadcast::channel(100);
|
||||||
let last_file_rebuild = Arc::new(Mutex::new(FileMap::new(config.crate_dir.clone())));
|
let last_file_rebuild = Arc::new(Mutex::new(FileMap::new(config.crate_dir.clone())));
|
||||||
|
@ -227,6 +237,7 @@ pub async fn startup_hot_reload(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// start serve dev-server at 0.0.0.0:8080
|
// start serve dev-server at 0.0.0.0:8080
|
||||||
print_console_info(
|
print_console_info(
|
||||||
port,
|
port,
|
||||||
|
@ -328,31 +339,32 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
.unwrap_or_else(|| vec![PathBuf::from("src")]);
|
||||||
|
|
||||||
let watcher_config = config.clone();
|
let watcher_config = config.clone();
|
||||||
let mut watcher = RecommendedWatcher::new(
|
let mut watcher = notify::recommended_watcher(move |info: notify::Result<notify::Event>| {
|
||||||
move |info: notify::Result<notify::Event>| {
|
let config = watcher_config.clone();
|
||||||
let config = watcher_config.clone();
|
if let Ok(e) = info {
|
||||||
if info.is_ok() {
|
if chrono::Local::now().timestamp() > last_update_time {
|
||||||
if chrono::Local::now().timestamp() > last_update_time {
|
match build_manager.rebuild() {
|
||||||
match build_manager.rebuild() {
|
Ok(res) => {
|
||||||
Ok(res) => {
|
last_update_time = chrono::Local::now().timestamp();
|
||||||
last_update_time = chrono::Local::now().timestamp();
|
print_console_info(
|
||||||
print_console_info(
|
port,
|
||||||
port,
|
&config,
|
||||||
&config,
|
PrettierOptions {
|
||||||
PrettierOptions {
|
changed: e.paths.clone(),
|
||||||
changed: info.unwrap().paths,
|
warnings: res.warnings,
|
||||||
warnings: res.warnings,
|
elapsed_time: res.elapsed_time,
|
||||||
elapsed_time: res.elapsed_time,
|
},
|
||||||
},
|
);
|
||||||
);
|
let _ = PluginManager::on_serve_rebuild(
|
||||||
}
|
chrono::Local::now().timestamp(),
|
||||||
Err(e) => log::error!("{}", e),
|
e.paths,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Err(e) => log::error!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
notify::Config::default(),
|
})
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for sub_path in allow_watch_path {
|
for sub_path in allow_watch_path {
|
||||||
|
@ -375,6 +387,8 @@ pub async fn startup_default(port: u16, config: CrateConfig) -> Result<()> {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PluginManager::on_serve_start(&config)?;
|
||||||
|
|
||||||
let file_service_config = config.clone();
|
let file_service_config = config.clone();
|
||||||
let file_service = ServiceBuilder::new()
|
let file_service = ServiceBuilder::new()
|
||||||
.and_then(
|
.and_then(
|
||||||
|
|
18
src/tools.rs
18
src/tools.rs
|
@ -18,9 +18,9 @@ pub enum Tool {
|
||||||
Tailwind,
|
Tailwind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tool_list() -> Vec<&'static str> {
|
// pub fn tool_list() -> Vec<&'static str> {
|
||||||
vec!["binaryen", "sass", "tailwindcss"]
|
// vec!["binaryen", "sass", "tailwindcss"]
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn app_path() -> PathBuf {
|
pub fn app_path() -> PathBuf {
|
||||||
let data_local = dirs::data_local_dir().unwrap();
|
let data_local = dirs::data_local_dir().unwrap();
|
||||||
|
@ -40,6 +40,16 @@ pub fn temp_path() -> PathBuf {
|
||||||
temp_path
|
temp_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clone_repo(dir: &Path, url: &str) -> anyhow::Result<()> {
|
||||||
|
let target_dir = dir.parent().unwrap();
|
||||||
|
let dir_name = dir.file_name().unwrap();
|
||||||
|
|
||||||
|
let mut cmd = Command::new("git");
|
||||||
|
let cmd = cmd.current_dir(target_dir);
|
||||||
|
let _res = cmd.arg("clone").arg(url).arg(dir_name).output()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tools_path() -> PathBuf {
|
pub fn tools_path() -> PathBuf {
|
||||||
let app_path = app_path();
|
let app_path = app_path();
|
||||||
let temp_path = app_path.join("tools");
|
let temp_path = app_path.join("tools");
|
||||||
|
@ -303,7 +313,7 @@ impl Tool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
|
pub fn extract_zip(file: &Path, target: &Path) -> anyhow::Result<()> {
|
||||||
let zip_file = std::fs::File::open(&file)?;
|
let zip_file = std::fs::File::open(&file)?;
|
||||||
let mut zip = zip::ZipArchive::new(zip_file)?;
|
let mut zip = zip::ZipArchive::new(zip_file)?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue