mirror of
https://github.com/nushell/nushell
synced 2024-12-25 12:33:17 +00:00
Start to Add WASM Support Again (#14418)
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> The [nushell/demo](https://github.com/nushell/demo) project successfully demonstrated running Nushell in the browser using WASM. However, the current version of Nushell cannot be easily built for the `wasm32-unknown-unknown` target, the default for `wasm-bindgen`. This PR introduces initial support for the `wasm32-unknown-unknown` target by disabling OS-dependent features such as filesystem access, IO, and platform/system-specific functionality. This separation is achieved using a new `os` feature in the following crates: - `nu-cmd-lang` - `nu-command` - `nu-engine` - `nu-protocol` The `os` feature includes all functionality that interacts with an operating system. It is enabled by default, but can be disabled using `--no-default-features`. All crates that depend on these core crates now use `--no-default-features` to allow compilation for WASM. To demonstrate compatibility, the following script builds all crates expected to work with WASM. Direct user interaction, running external commands, working with plugins, and features requiring `openssl` are out of scope for now due to their complexity or reliance on C libraries, which are difficult to compile and link in a WASM environment. ```nushell [ # compatible crates "nu-cmd-base", "nu-cmd-extra", "nu-cmd-lang", "nu-color-config", "nu-command", "nu-derive-value", "nu-engine", "nu-glob", "nu-json", "nu-parser", "nu-path", "nu-pretty-hex", "nu-protocol", "nu-std", "nu-system", "nu-table", "nu-term-grid", "nu-utils", "nuon" ] | each {cargo build -p $in --target wasm32-unknown-unknown --no-default-features} ``` ## Caveats This PR has a few caveats: 1. **`miette` and `terminal-size` Dependency Issue** `miette` depends on `terminal-size`, which uses `rustix` when the target is not Windows. However, `rustix` requires `std::os::unix`, which is unavailable in WASM. To address this, I opened a [PR](https://github.com/eminence/terminal-size/pull/68) for `terminal-size` to conditionally compile `rustix` only when the target is Unix. For now, the `Cargo.toml` includes patches to: - Use my forked version of `terminal-size`. - ~~Use an unreleased version of `miette` that depends on `terminal-size@0.4`.~~ These patches are temporary and can be removed once the upstream changes are merged and released. 2. **Test Output Adjustments** Due to the slight bump in the `miette` version, one test required adjustments to accommodate minor formatting changes in the error output, such as shifted newlines. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> This shouldn't break anything but allows using some crates for targeting `wasm32-unknown-unknown` to revive the demo page eventually. # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` I did not add any extra tests, I just checked that compiling works, also when using the host target but unselecting the `os` feature. # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --> ~~Breaking the wasm support can be easily done by adding some `use`s or by adding a new dependency, we should definitely add some CI that also at least builds against wasm to make sure that building for it keep working.~~ I added a job to build wasm. --------- Co-authored-by: Ian Manske <ian.manske@pm.me>
This commit is contained in:
parent
07a37f9b47
commit
3d5f853b03
48 changed files with 490 additions and 234 deletions
31
.github/workflows/ci.yml
vendored
31
.github/workflows/ci.yml
vendored
|
@ -162,3 +162,34 @@ jobs:
|
|||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
||||
|
||||
build-wasm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: Add wasm32-unknown-unknown target
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
|
||||
- run: cargo build -p nu-cmd-base --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-cmd-extra --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-cmd-lang --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-color-config --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-command --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-derive-value --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-engine --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-glob --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-json --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-parser --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-path --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-pretty-hex --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-protocol --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-std --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-system --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-table --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-term-grid --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nu-utils --no-default-features --target wasm32-unknown-unknown
|
||||
- run: cargo build -p nuon --no-default-features --target wasm32-unknown-unknown
|
||||
|
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -2647,7 +2647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3368,6 +3368,7 @@ dependencies = [
|
|||
"fancy-regex",
|
||||
"filesize",
|
||||
"filetime",
|
||||
"getrandom",
|
||||
"human-date-parser",
|
||||
"indexmap",
|
||||
"indicatif",
|
||||
|
@ -3755,6 +3756,7 @@ dependencies = [
|
|||
name = "nu-utils"
|
||||
version = "0.100.1"
|
||||
dependencies = [
|
||||
"crossterm 0.28.1",
|
||||
"crossterm_winapi",
|
||||
"fancy-regex",
|
||||
"log",
|
||||
|
@ -6847,9 +6849,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
|
||||
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
|
@ -7874,7 +7876,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -134,6 +134,7 @@ quickcheck = "1.0"
|
|||
quickcheck_macros = "1.0"
|
||||
quote = "1.0"
|
||||
rand = "0.8"
|
||||
getrandom = "0.2" # pick same version that rand requires
|
||||
rand_chacha = "0.3.1"
|
||||
ratatui = "0.26"
|
||||
rayon = "1.10"
|
||||
|
|
|
@ -19,11 +19,11 @@ tempfile = { workspace = true }
|
|||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", features = ["os"] }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.100.1", optional = true }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", features = ["os"] }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.100.1" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
|
|
|
@ -13,10 +13,10 @@ version = "0.100.1"
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
|
||||
indexmap = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
|
|
|
@ -17,12 +17,12 @@ workspace = true
|
|||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-json = { version = "0.100.1", path = "../nu-json" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-pretty-hex = { version = "0.100.1", path = "../nu-pretty-hex" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
|
||||
# Potential dependencies for extras
|
||||
heck = { workspace = true }
|
||||
|
|
|
@ -15,10 +15,10 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
|
||||
itertools = { workspace = true }
|
||||
shadow-rs = { version = "0.36", default-features = false }
|
||||
|
@ -27,6 +27,17 @@ shadow-rs = { version = "0.36", default-features = false }
|
|||
shadow-rs = { version = "0.36", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["os"]
|
||||
os = [
|
||||
"nu-engine/os",
|
||||
"nu-protocol/os",
|
||||
"nu-utils/os",
|
||||
]
|
||||
plugin = [
|
||||
"nu-protocol/plugin",
|
||||
"os",
|
||||
]
|
||||
|
||||
mimalloc = []
|
||||
trash-support = []
|
||||
sqlite = []
|
||||
|
|
|
@ -169,6 +169,7 @@ fn run(
|
|||
let origin = match stream.source() {
|
||||
ByteStreamSource::Read(_) => "unknown",
|
||||
ByteStreamSource::File(_) => "file",
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(_) => "external",
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_env};
|
||||
use nu_protocol::{
|
||||
engine::Closure,
|
||||
process::{ChildPipe, ChildProcess},
|
||||
ByteStream, ByteStreamSource, OutDest,
|
||||
};
|
||||
#[cfg(feature = "os")]
|
||||
use nu_protocol::process::{ChildPipe, ChildProcess};
|
||||
use nu_protocol::{engine::Closure, ByteStream, ByteStreamSource, OutDest};
|
||||
|
||||
use std::{
|
||||
io::{Cursor, Read},
|
||||
thread,
|
||||
|
@ -119,6 +118,13 @@ impl Command for Do {
|
|||
match result {
|
||||
Ok(PipelineData::ByteStream(stream, metadata)) if capture_errors => {
|
||||
let span = stream.span();
|
||||
#[cfg(not(feature = "os"))]
|
||||
return Err(ShellError::DisabledOsSupport {
|
||||
msg: "Cannot create a thread to receive stdout message.".to_string(),
|
||||
span: Some(span),
|
||||
});
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
match stream.into_child() {
|
||||
Ok(mut child) => {
|
||||
// Use a thread to receive stdout message.
|
||||
|
@ -196,6 +202,7 @@ impl Command for Do {
|
|||
OutDest::Pipe | OutDest::PipeSeparate | OutDest::Value
|
||||
) =>
|
||||
{
|
||||
#[cfg(feature = "os")]
|
||||
if let ByteStreamSource::Child(child) = stream.source_mut() {
|
||||
child.ignore_error(true);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ impl Command for Ignore {
|
|||
mut input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
if let PipelineData::ByteStream(stream, _) = &mut input {
|
||||
#[cfg(feature = "os")]
|
||||
if let ByteStreamSource::Child(child) = stream.source_mut() {
|
||||
child.ignore_error(true);
|
||||
}
|
||||
|
|
|
@ -116,6 +116,11 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
|
|||
Value::string(features_enabled().join(", "), span),
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "plugin"))]
|
||||
let _ = engine_state;
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
{
|
||||
// Get a list of plugin names and versions if present
|
||||
let installed_plugins = engine_state
|
||||
.plugins()
|
||||
|
@ -134,6 +139,7 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
|
|||
"installed_plugins",
|
||||
Value::string(installed_plugins.join(", "), span),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Value::record(record, span).into_pipeline_data())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(not(feature = "os"), allow(unused))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod core_commands;
|
||||
mod default_context;
|
||||
|
|
|
@ -14,8 +14,8 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-json = { path = "../nu-json", version = "0.100.1" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
|
||||
|
|
|
@ -18,17 +18,17 @@ workspace = true
|
|||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.100.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-glob = { path = "../nu-glob", version = "0.100.1" }
|
||||
nu-json = { path = "../nu-json", version = "0.100.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-system = { path = "../nu-system", version = "0.100.1" }
|
||||
nu-table = { path = "../nu-table", version = "0.100.1" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
nu-ansi-term = { workspace = true }
|
||||
nuon = { path = "../nuon", version = "0.100.1" }
|
||||
|
||||
|
@ -43,7 +43,7 @@ chardetng = { workspace = true }
|
|||
chrono = { workspace = true, features = ["std", "unstable-locales", "clock"], default-features = false }
|
||||
chrono-humanize = { workspace = true }
|
||||
chrono-tz = { workspace = true }
|
||||
crossterm = { workspace = true }
|
||||
crossterm = { workspace = true, optional = true }
|
||||
csv = { workspace = true }
|
||||
dialoguer = { workspace = true, default-features = false, features = ["fuzzy-select"] }
|
||||
digest = { workspace = true, default-features = false }
|
||||
|
@ -61,19 +61,20 @@ lscolors = { workspace = true, default-features = false, features = ["nu-ansi-te
|
|||
md5 = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
mime_guess = { workspace = true }
|
||||
multipart-rs = { workspace = true }
|
||||
native-tls = { workspace = true }
|
||||
notify-debouncer-full = { workspace = true, default-features = false }
|
||||
multipart-rs = { workspace = true, optional = true }
|
||||
native-tls = { workspace = true, optional = true }
|
||||
notify-debouncer-full = { workspace = true, default-features = false, optional = true }
|
||||
num-format = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
oem_cp = { workspace = true }
|
||||
open = { workspace = true }
|
||||
os_pipe = { workspace = true }
|
||||
open = { workspace = true, optional = true }
|
||||
os_pipe = { workspace = true, optional = true }
|
||||
pathdiff = { workspace = true }
|
||||
percent-encoding = { workspace = true }
|
||||
print-positions = { workspace = true }
|
||||
quick-xml = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand = { workspace = true, optional = true }
|
||||
getrandom = { workspace = true, optional = true }
|
||||
rayon = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
roxmltree = { workspace = true }
|
||||
|
@ -89,26 +90,26 @@ tabled = { workspace = true, features = ["ansi"], default-features = false }
|
|||
titlecase = { workspace = true }
|
||||
toml = { workspace = true, features = ["preserve_order"] }
|
||||
unicode-segmentation = { workspace = true }
|
||||
ureq = { workspace = true, default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
||||
ureq = { workspace = true, default-features = false, features = ["charset", "gzip", "json"] }
|
||||
url = { workspace = true }
|
||||
uu_cp = { workspace = true }
|
||||
uu_mkdir = { workspace = true }
|
||||
uu_mktemp = { workspace = true }
|
||||
uu_mv = { workspace = true }
|
||||
uu_touch = { workspace = true }
|
||||
uu_uname = { workspace = true }
|
||||
uu_whoami = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
uu_cp = { workspace = true, optional = true }
|
||||
uu_mkdir = { workspace = true, optional = true }
|
||||
uu_mktemp = { workspace = true, optional = true }
|
||||
uu_mv = { workspace = true, optional = true }
|
||||
uu_touch = { workspace = true, optional = true }
|
||||
uu_uname = { workspace = true, optional = true }
|
||||
uu_whoami = { workspace = true, optional = true }
|
||||
uuid = { workspace = true, features = ["v4"], optional = true }
|
||||
v_htmlescape = { workspace = true }
|
||||
wax = { workspace = true }
|
||||
which = { workspace = true }
|
||||
which = { workspace = true, optional = true }
|
||||
unicode-width = { workspace = true }
|
||||
data-encoding = { version = "2.6.0", features = ["alloc"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winreg = { workspace = true }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
[target.'cfg(all(not(windows), not(target_arch = "wasm32")))'.dependencies]
|
||||
uucore = { workspace = true, features = ["mode"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
@ -134,7 +135,53 @@ features = [
|
|||
workspace = true
|
||||
|
||||
[features]
|
||||
plugin = ["nu-parser/plugin"]
|
||||
default = ["os"]
|
||||
os = [
|
||||
# include other features
|
||||
"js",
|
||||
"network",
|
||||
"nu-protocol/os",
|
||||
"nu-utils/os",
|
||||
|
||||
# os-dependant dependencies
|
||||
"crossterm",
|
||||
"notify-debouncer-full",
|
||||
"open",
|
||||
"os_pipe",
|
||||
"uu_cp",
|
||||
"uu_mkdir",
|
||||
"uu_mktemp",
|
||||
"uu_mv",
|
||||
"uu_touch",
|
||||
"uu_uname",
|
||||
"uu_whoami",
|
||||
"which",
|
||||
]
|
||||
|
||||
# The dependencies listed below need 'getrandom'.
|
||||
# They work with JS (usually with wasm-bindgen) or regular OS support.
|
||||
# Hence they are also put under the 'os' feature to avoid repetition.
|
||||
js = [
|
||||
"getrandom",
|
||||
"getrandom/js",
|
||||
"rand",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
# These dependencies require networking capabilities, especially the http
|
||||
# interface requires openssl which is not easy to embed into wasm,
|
||||
# using rustls could solve this issue.
|
||||
network = [
|
||||
"multipart-rs",
|
||||
"native-tls",
|
||||
"ureq/native-tls",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
plugin = [
|
||||
"nu-parser/plugin",
|
||||
"os",
|
||||
]
|
||||
sqlite = ["rusqlite"]
|
||||
trash-support = ["trash"]
|
||||
|
||||
|
|
|
@ -177,4 +177,9 @@ fn get_thread_id() -> u64 {
|
|||
{
|
||||
nix::sys::pthread::pthread_self() as u64
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
// wasm doesn't have any threads accessible, so we return 0 as a fallback
|
||||
0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::inspect_table;
|
||||
use crossterm::terminal::size;
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_utils::terminal_size;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Inspect;
|
||||
|
@ -38,7 +38,7 @@ impl Command for Inspect {
|
|||
let original_input = input_val.clone();
|
||||
let description = input_val.get_type().to_string();
|
||||
|
||||
let (cols, _rows) = size().unwrap_or((0, 0));
|
||||
let (cols, _rows) = terminal_size().unwrap_or((0, 0));
|
||||
|
||||
let table = inspect_table::build_table(input_val, description, cols as usize);
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
}
|
||||
|
||||
// Filters
|
||||
#[cfg(feature = "rand")]
|
||||
bind_command! {
|
||||
Shuffle
|
||||
}
|
||||
bind_command! {
|
||||
All,
|
||||
Any,
|
||||
|
@ -72,7 +76,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
Rename,
|
||||
Reverse,
|
||||
Select,
|
||||
Shuffle,
|
||||
Skip,
|
||||
SkipUntil,
|
||||
SkipWhile,
|
||||
|
@ -114,6 +117,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
};
|
||||
|
||||
// System
|
||||
#[cfg(feature = "os")]
|
||||
bind_command! {
|
||||
Complete,
|
||||
External,
|
||||
|
@ -161,10 +165,12 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
ViewSpan,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(all(feature = "os", windows))]
|
||||
bind_command! { RegistryQuery }
|
||||
|
||||
#[cfg(any(
|
||||
#[cfg(all(
|
||||
feature = "os",
|
||||
any(
|
||||
target_os = "android",
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
|
@ -172,6 +178,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
target_os = "openbsd",
|
||||
target_os = "macos",
|
||||
target_os = "windows"
|
||||
)
|
||||
))]
|
||||
bind_command! { Ps };
|
||||
|
||||
|
@ -219,6 +226,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
};
|
||||
|
||||
// FileSystem
|
||||
#[cfg(feature = "os")]
|
||||
bind_command! {
|
||||
Cd,
|
||||
Ls,
|
||||
|
@ -237,6 +245,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
};
|
||||
|
||||
// Platform
|
||||
#[cfg(feature = "os")]
|
||||
bind_command! {
|
||||
Ansi,
|
||||
AnsiLink,
|
||||
|
@ -255,7 +264,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
Whoami,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(all(unix, feature = "os"))]
|
||||
bind_command! { ULimit };
|
||||
|
||||
// Date
|
||||
|
@ -380,6 +389,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
}
|
||||
|
||||
// Network
|
||||
#[cfg(feature = "network")]
|
||||
bind_command! {
|
||||
Http,
|
||||
HttpDelete,
|
||||
|
@ -389,6 +399,9 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
HttpPost,
|
||||
HttpPut,
|
||||
HttpOptions,
|
||||
Port,
|
||||
}
|
||||
bind_command! {
|
||||
Url,
|
||||
UrlBuildQuery,
|
||||
UrlSplitQuery,
|
||||
|
@ -396,10 +409,10 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||
UrlEncode,
|
||||
UrlJoin,
|
||||
UrlParse,
|
||||
Port,
|
||||
}
|
||||
|
||||
// Random
|
||||
#[cfg(feature = "rand")]
|
||||
bind_command! {
|
||||
Random,
|
||||
RandomBool,
|
||||
|
|
80
crates/nu-command/src/env/config/config_.rs
vendored
80
crates/nu-command/src/env/config/config_.rs
vendored
|
@ -1,4 +1,6 @@
|
|||
use nu_engine::{command_prelude::*, get_full_help};
|
||||
use nu_cmd_base::util::get_editor;
|
||||
use nu_engine::{command_prelude::*, env_to_strings, get_full_help};
|
||||
use nu_system::ForegroundChild;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigMeta;
|
||||
|
@ -36,3 +38,79 @@ impl Command for ConfigMeta {
|
|||
vec!["options", "setup"]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "os"))]
|
||||
pub(super) fn start_editor(
|
||||
_: &'static str,
|
||||
_: &EngineState,
|
||||
_: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
Err(ShellError::DisabledOsSupport {
|
||||
msg: "Running external commands is not available without OS support.".to_string(),
|
||||
span: Some(call.head),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
pub(super) fn start_editor(
|
||||
config_path: &'static str,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// Find the editor executable.
|
||||
let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
|
||||
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
|
||||
let cwd = engine_state.cwd(Some(stack))?;
|
||||
let editor_executable =
|
||||
crate::which(&editor_name, &paths, cwd.as_ref()).ok_or(ShellError::ExternalCommand {
|
||||
label: format!("`{editor_name}` not found"),
|
||||
help: "Failed to find the editor executable".into(),
|
||||
span: call.head,
|
||||
})?;
|
||||
|
||||
let Some(config_path) = engine_state.get_config_path(config_path) else {
|
||||
return Err(ShellError::GenericError {
|
||||
error: format!("Could not find $nu.{config_path}"),
|
||||
msg: format!("Could not find $nu.{config_path}"),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
});
|
||||
};
|
||||
let config_path = config_path.to_string_lossy().to_string();
|
||||
|
||||
// Create the command.
|
||||
let mut command = std::process::Command::new(editor_executable);
|
||||
|
||||
// Configure PWD.
|
||||
command.current_dir(cwd);
|
||||
|
||||
// Configure environment variables.
|
||||
let envs = env_to_strings(engine_state, stack)?;
|
||||
command.env_clear();
|
||||
command.envs(envs);
|
||||
|
||||
// Configure args.
|
||||
command.arg(config_path);
|
||||
command.args(editor_args);
|
||||
|
||||
// Spawn the child process. On Unix, also put the child process to
|
||||
// foreground if we're in an interactive session.
|
||||
#[cfg(windows)]
|
||||
let child = ForegroundChild::spawn(command)?;
|
||||
#[cfg(unix)]
|
||||
let child = ForegroundChild::spawn(
|
||||
command,
|
||||
engine_state.is_interactive,
|
||||
&engine_state.pipeline_externals_state,
|
||||
)?;
|
||||
|
||||
// Wrap the output into a `PipelineData::ByteStream`.
|
||||
let child = nu_protocol::process::ChildProcess::new(child, None, false, call.head)?;
|
||||
Ok(PipelineData::ByteStream(
|
||||
ByteStream::child(child, call.head),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
|
61
crates/nu-command/src/env/config/config_env.rs
vendored
61
crates/nu-command/src/env/config/config_env.rs
vendored
|
@ -1,7 +1,4 @@
|
|||
use nu_cmd_base::util::get_editor;
|
||||
use nu_engine::{command_prelude::*, env_to_strings};
|
||||
use nu_protocol::{process::ChildProcess, ByteStream};
|
||||
use nu_system::ForegroundChild;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigEnv;
|
||||
|
@ -81,60 +78,6 @@ impl Command for ConfigEnv {
|
|||
return Ok(Value::string(nu_utils::get_sample_env(), head).into_pipeline_data());
|
||||
}
|
||||
|
||||
// Find the editor executable.
|
||||
let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
|
||||
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
|
||||
let cwd = engine_state.cwd(Some(stack))?;
|
||||
let editor_executable = crate::which(&editor_name, &paths, cwd.as_ref()).ok_or(
|
||||
ShellError::ExternalCommand {
|
||||
label: format!("`{editor_name}` not found"),
|
||||
help: "Failed to find the editor executable".into(),
|
||||
span: call.head,
|
||||
},
|
||||
)?;
|
||||
|
||||
let Some(env_path) = engine_state.get_config_path("env-path") else {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Could not find $nu.env-path".into(),
|
||||
msg: "Could not find $nu.env-path".into(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
});
|
||||
};
|
||||
let env_path = env_path.to_string_lossy().to_string();
|
||||
|
||||
// Create the command.
|
||||
let mut command = std::process::Command::new(editor_executable);
|
||||
|
||||
// Configure PWD.
|
||||
command.current_dir(cwd);
|
||||
|
||||
// Configure environment variables.
|
||||
let envs = env_to_strings(engine_state, stack)?;
|
||||
command.env_clear();
|
||||
command.envs(envs);
|
||||
|
||||
// Configure args.
|
||||
command.arg(env_path);
|
||||
command.args(editor_args);
|
||||
|
||||
// Spawn the child process. On Unix, also put the child process to
|
||||
// foreground if we're in an interactive session.
|
||||
#[cfg(windows)]
|
||||
let child = ForegroundChild::spawn(command)?;
|
||||
#[cfg(unix)]
|
||||
let child = ForegroundChild::spawn(
|
||||
command,
|
||||
engine_state.is_interactive,
|
||||
&engine_state.pipeline_externals_state,
|
||||
)?;
|
||||
|
||||
// Wrap the output into a `PipelineData::ByteStream`.
|
||||
let child = ChildProcess::new(child, None, false, call.head)?;
|
||||
Ok(PipelineData::ByteStream(
|
||||
ByteStream::child(child, call.head),
|
||||
None,
|
||||
))
|
||||
super::config_::start_editor("env-path", engine_state, stack, call)
|
||||
}
|
||||
}
|
||||
|
|
61
crates/nu-command/src/env/config/config_nu.rs
vendored
61
crates/nu-command/src/env/config/config_nu.rs
vendored
|
@ -1,7 +1,4 @@
|
|||
use nu_cmd_base::util::get_editor;
|
||||
use nu_engine::{command_prelude::*, env_to_strings};
|
||||
use nu_protocol::{process::ChildProcess, ByteStream};
|
||||
use nu_system::ForegroundChild;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigNu;
|
||||
|
@ -83,60 +80,6 @@ impl Command for ConfigNu {
|
|||
return Ok(Value::string(nu_utils::get_sample_config(), head).into_pipeline_data());
|
||||
}
|
||||
|
||||
// Find the editor executable.
|
||||
let (editor_name, editor_args) = get_editor(engine_state, stack, call.head)?;
|
||||
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
|
||||
let cwd = engine_state.cwd(Some(stack))?;
|
||||
let editor_executable = crate::which(&editor_name, &paths, cwd.as_ref()).ok_or(
|
||||
ShellError::ExternalCommand {
|
||||
label: format!("`{editor_name}` not found"),
|
||||
help: "Failed to find the editor executable".into(),
|
||||
span: call.head,
|
||||
},
|
||||
)?;
|
||||
|
||||
let Some(config_path) = engine_state.get_config_path("config-path") else {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "Could not find $nu.config-path".into(),
|
||||
msg: "Could not find $nu.config-path".into(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
});
|
||||
};
|
||||
let config_path = config_path.to_string_lossy().to_string();
|
||||
|
||||
// Create the command.
|
||||
let mut command = std::process::Command::new(editor_executable);
|
||||
|
||||
// Configure PWD.
|
||||
command.current_dir(cwd);
|
||||
|
||||
// Configure environment variables.
|
||||
let envs = env_to_strings(engine_state, stack)?;
|
||||
command.env_clear();
|
||||
command.envs(envs);
|
||||
|
||||
// Configure args.
|
||||
command.arg(config_path);
|
||||
command.args(editor_args);
|
||||
|
||||
// Spawn the child process. On Unix, also put the child process to
|
||||
// foreground if we're in an interactive session.
|
||||
#[cfg(windows)]
|
||||
let child = ForegroundChild::spawn(command)?;
|
||||
#[cfg(unix)]
|
||||
let child = ForegroundChild::spawn(
|
||||
command,
|
||||
engine_state.is_interactive,
|
||||
&engine_state.pipeline_externals_state,
|
||||
)?;
|
||||
|
||||
// Wrap the output into a `PipelineData::ByteStream`.
|
||||
let child = ChildProcess::new(child, None, false, call.head)?;
|
||||
Ok(PipelineData::ByteStream(
|
||||
ByteStream::child(child, call.head),
|
||||
None,
|
||||
))
|
||||
super::config_::start_editor("config-path", engine_state, stack, call)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,3 +103,9 @@ fn is_root_impl() -> bool {
|
|||
|
||||
elevated
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn is_root_impl() -> bool {
|
||||
// in wasm we don't have a user system, so technically we are never root
|
||||
false
|
||||
}
|
||||
|
|
|
@ -102,6 +102,7 @@ impl Command for Save {
|
|||
ByteStreamSource::File(source) => {
|
||||
stream_to_file(source, size, signals, file, span, progress)?;
|
||||
}
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(mut child) => {
|
||||
fn write_or_consume_stderr(
|
||||
stderr: ChildPipe,
|
||||
|
|
|
@ -37,6 +37,7 @@ mod reject;
|
|||
mod rename;
|
||||
mod reverse;
|
||||
mod select;
|
||||
#[cfg(feature = "rand")]
|
||||
mod shuffle;
|
||||
mod skip;
|
||||
mod sort;
|
||||
|
@ -95,6 +96,7 @@ pub use reject::Reject;
|
|||
pub use rename::Rename;
|
||||
pub use reverse::Reverse;
|
||||
pub use select::Select;
|
||||
#[cfg(feature = "rand")]
|
||||
pub use shuffle::Shuffle;
|
||||
pub use skip::*;
|
||||
pub use sort::Sort;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
|
||||
#[cfg(feature = "os")]
|
||||
use nu_protocol::process::ChildPipe;
|
||||
use nu_protocol::{
|
||||
byte_stream::copy_with_signals, engine::Closure, process::ChildPipe, report_shell_error,
|
||||
ByteStream, ByteStreamSource, OutDest, PipelineMetadata, Signals,
|
||||
byte_stream::copy_with_signals, engine::Closure, report_shell_error, ByteStream,
|
||||
ByteStreamSource, OutDest, PipelineMetadata, Signals,
|
||||
};
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
|
@ -152,6 +154,7 @@ use it in your pipeline."#
|
|||
metadata,
|
||||
))
|
||||
}
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(mut child) => {
|
||||
let stderr_thread = if use_stderr {
|
||||
let stderr_thread = if let Some(stderr) = child.stderr.take() {
|
||||
|
@ -454,6 +457,7 @@ fn copy(src: impl Read, dest: impl Write, info: &StreamInfo) -> Result<(), Shell
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
fn copy_pipe(pipe: ChildPipe, dest: impl Write, info: &StreamInfo) -> Result<(), ShellError> {
|
||||
match pipe {
|
||||
ChildPipe::Pipe(pipe) => copy(pipe, dest, info),
|
||||
|
@ -477,6 +481,7 @@ fn copy_on_thread(
|
|||
.map_err(|e| e.into_spanned(span).into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "os")]
|
||||
fn copy_pipe_on_thread(
|
||||
pipe: ChildPipe,
|
||||
dest: impl Write + Send + 'static,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(not(feature = "os"), allow(unused))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod bytes;
|
||||
mod charting;
|
||||
|
@ -8,6 +9,7 @@ mod default_context;
|
|||
mod env;
|
||||
mod example_test;
|
||||
mod experimental;
|
||||
#[cfg(feature = "os")]
|
||||
mod filesystem;
|
||||
mod filters;
|
||||
mod formats;
|
||||
|
@ -18,8 +20,10 @@ mod math;
|
|||
mod misc;
|
||||
mod network;
|
||||
mod path;
|
||||
#[cfg(feature = "os")]
|
||||
mod platform;
|
||||
mod progress_bar;
|
||||
#[cfg(feature = "rand")]
|
||||
mod random;
|
||||
mod removed;
|
||||
mod shells;
|
||||
|
@ -27,6 +31,7 @@ mod sort_utils;
|
|||
#[cfg(feature = "sqlite")]
|
||||
mod stor;
|
||||
mod strings;
|
||||
#[cfg(feature = "os")]
|
||||
mod system;
|
||||
mod viewers;
|
||||
|
||||
|
@ -40,6 +45,7 @@ pub use env::*;
|
|||
#[cfg(test)]
|
||||
pub use example_test::{test_examples, test_examples_with_commands};
|
||||
pub use experimental::*;
|
||||
#[cfg(feature = "os")]
|
||||
pub use filesystem::*;
|
||||
pub use filters::*;
|
||||
pub use formats::*;
|
||||
|
@ -50,7 +56,9 @@ pub use math::*;
|
|||
pub use misc::*;
|
||||
pub use network::*;
|
||||
pub use path::*;
|
||||
#[cfg(feature = "os")]
|
||||
pub use platform::*;
|
||||
#[cfg(feature = "rand")]
|
||||
pub use random::*;
|
||||
pub use removed::*;
|
||||
pub use shells::*;
|
||||
|
@ -58,6 +66,7 @@ pub use sort_utils::*;
|
|||
#[cfg(feature = "sqlite")]
|
||||
pub use stor::*;
|
||||
pub use strings::*;
|
||||
#[cfg(feature = "os")]
|
||||
pub use system::*;
|
||||
pub use viewers::*;
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
#[cfg(feature = "network")]
|
||||
mod http;
|
||||
#[cfg(feature = "network")]
|
||||
mod port;
|
||||
mod url;
|
||||
|
||||
#[cfg(feature = "network")]
|
||||
pub use self::http::*;
|
||||
pub use self::url::*;
|
||||
|
||||
#[cfg(feature = "network")]
|
||||
pub use port::SubCommand as Port;
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// use super::icons::{icon_for_file, iconify_style_ansi_to_nu};
|
||||
use super::icons::icon_for_file;
|
||||
use crossterm::terminal::size;
|
||||
use lscolors::Style;
|
||||
use nu_engine::{command_prelude::*, env_to_string};
|
||||
use nu_protocol::Config;
|
||||
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
||||
use nu_utils::get_ls_colors;
|
||||
use nu_utils::{get_ls_colors, terminal_size};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -192,7 +191,7 @@ fn create_grid_output(
|
|||
|
||||
let cols = if let Some(col) = width_param {
|
||||
col as u16
|
||||
} else if let Ok((w, _h)) = size() {
|
||||
} else if let Ok((w, _h)) = terminal_size() {
|
||||
w
|
||||
} else {
|
||||
80u16
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// overall reduce the redundant calls to StyleComputer etc.
|
||||
// the goal is to configure it once...
|
||||
|
||||
use crossterm::terminal::size;
|
||||
use lscolors::{LsColors, Style};
|
||||
use nu_color_config::{color_from_hex, StyleComputer, TextStyle};
|
||||
use nu_engine::{command_prelude::*, env_to_string};
|
||||
|
@ -15,7 +14,7 @@ use nu_table::{
|
|||
common::create_nu_table_config, CollapsedTable, ExpandedTable, JustTable, NuTable, NuTableCell,
|
||||
StringResult, TableOpts, TableOutput,
|
||||
};
|
||||
use nu_utils::get_ls_colors;
|
||||
use nu_utils::{get_ls_colors, terminal_size};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io::{IsTerminal, Read},
|
||||
|
@ -30,7 +29,7 @@ const STREAM_PAGE_SIZE: usize = 1000;
|
|||
fn get_width_param(width_param: Option<i64>) -> usize {
|
||||
if let Some(col) = width_param {
|
||||
col as usize
|
||||
} else if let Ok((w, _h)) = size() {
|
||||
} else if let Ok((w, _h)) = terminal_size() {
|
||||
w as usize
|
||||
} else {
|
||||
80
|
||||
|
@ -712,6 +711,13 @@ fn make_clickable_link(
|
|||
) -> String {
|
||||
// uri's based on this https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
|
||||
|
||||
#[cfg(any(
|
||||
unix,
|
||||
windows,
|
||||
target_os = "redox",
|
||||
target_os = "wasi",
|
||||
target_os = "hermit"
|
||||
))]
|
||||
if show_clickable_links {
|
||||
format!(
|
||||
"\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\",
|
||||
|
@ -727,6 +733,18 @@ fn make_clickable_link(
|
|||
None => full_path,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(
|
||||
unix,
|
||||
windows,
|
||||
target_os = "redox",
|
||||
target_os = "wasi",
|
||||
target_os = "hermit"
|
||||
)))]
|
||||
match link_name {
|
||||
Some(link_name) => link_name.to_string(),
|
||||
None => full_path,
|
||||
}
|
||||
}
|
||||
|
||||
struct PagingTableCreator {
|
||||
|
|
|
@ -14,12 +14,20 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", features = ["plugin"], version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
log = { workspace = true }
|
||||
terminal_size = { workspace = true }
|
||||
|
||||
[features]
|
||||
plugin = []
|
||||
default = ["os"]
|
||||
os = [
|
||||
"nu-protocol/os",
|
||||
"nu-utils/os",
|
||||
]
|
||||
plugin = [
|
||||
"nu-protocol/plugin",
|
||||
"os",
|
||||
]
|
||||
|
|
|
@ -8,9 +8,9 @@ use nu_protocol::{
|
|||
Argument, Closure, EngineState, ErrorHandler, Matcher, Redirection, Stack, StateWorkingSet,
|
||||
},
|
||||
ir::{Call, DataSlice, Instruction, IrAstRef, IrBlock, Literal, RedirectMode},
|
||||
ByteStreamSource, DataSource, DeclId, ErrSpan, Flag, IntoPipelineData, IntoSpanned, ListStream,
|
||||
OutDest, PipelineData, PipelineMetadata, PositionalArg, Range, Record, RegId, ShellError,
|
||||
Signals, Signature, Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
DataSource, DeclId, ErrSpan, Flag, IntoPipelineData, IntoSpanned, ListStream, OutDest,
|
||||
PipelineData, PipelineMetadata, PositionalArg, Range, Record, RegId, ShellError, Signals,
|
||||
Signature, Span, Spanned, Type, Value, VarId, ENV_VARIABLE_ID,
|
||||
};
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
|
||||
|
@ -478,8 +478,9 @@ fn eval_instruction<D: DebugContext>(
|
|||
Ok(Continue)
|
||||
}
|
||||
Instruction::CheckErrRedirected { src } => match ctx.borrow_reg(*src) {
|
||||
#[cfg(feature = "os")]
|
||||
PipelineData::ByteStream(stream, _)
|
||||
if matches!(stream.source(), ByteStreamSource::Child(_)) =>
|
||||
if matches!(stream.source(), nu_protocol::ByteStreamSource::Child(_)) =>
|
||||
{
|
||||
Ok(Continue)
|
||||
}
|
||||
|
@ -513,7 +514,7 @@ fn eval_instruction<D: DebugContext>(
|
|||
span: Some(*span),
|
||||
})?;
|
||||
let is_external = if let PipelineData::ByteStream(stream, ..) = &src {
|
||||
matches!(stream.source(), ByteStreamSource::Child(..))
|
||||
stream.source().is_external()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
|
@ -15,11 +15,11 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", optional = true, version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
|
||||
bytesize = { workspace = true }
|
||||
chrono = { default-features = false, features = ['std'], workspace = true }
|
||||
|
|
|
@ -60,6 +60,7 @@ fn expand_tilde_with_home(path: impl AsRef<Path>, home: Option<PathBuf>) -> Path
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn fallback_home_dir(username: &str) -> PathBuf {
|
||||
PathBuf::from_iter([FALLBACK_USER_HOME_BASE_DIR, username])
|
||||
}
|
||||
|
@ -110,6 +111,13 @@ fn user_home_dir(username: &str) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn user_home_dir(_: &str) -> PathBuf {
|
||||
// if WASI is used, we try to get a home dir via HOME env, otherwise we don't have a home dir
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/".to_string());
|
||||
PathBuf::from(home)
|
||||
}
|
||||
|
||||
/// Returns true if the shell is running inside the Termux terminal emulator
|
||||
/// app.
|
||||
#[cfg(target_os = "android")]
|
||||
|
|
|
@ -36,6 +36,13 @@ pub fn has_trailing_slash(path: &Path) -> bool {
|
|||
last == Some(&b'/')
|
||||
}
|
||||
|
||||
/// `true` if the path has a trailing slash, including if it's the root directory.
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn has_trailing_slash(path: &Path) -> bool {
|
||||
// in the web paths are often just URLs, they are separated by forward slashes
|
||||
path.to_str().map_or(false, |s| s.ends_with('/'))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -14,8 +14,8 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", features = ["plugin"] }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", features = ["plugin"] }
|
||||
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.100.1" }
|
||||
nu-plugin-core = { path = "../nu-plugin-core", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
|
|
|
@ -16,7 +16,7 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
nu-path = { path = "../nu-path", version = "0.100.1" }
|
||||
nu-system = { path = "../nu-system", version = "0.100.1" }
|
||||
nu-derive-value = { path = "../nu-derive-value", version = "0.100.1" }
|
||||
|
@ -38,7 +38,7 @@ serde = { workspace = true }
|
|||
serde_json = { workspace = true }
|
||||
thiserror = "2.0"
|
||||
typetag = "0.2"
|
||||
os_pipe = { workspace = true, features = ["io_safety"] }
|
||||
os_pipe = { workspace = true, optional = true, features = ["io_safety"] }
|
||||
log = { workspace = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
|
@ -49,8 +49,15 @@ dirs-sys = { workspace = true }
|
|||
windows-sys = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["os"]
|
||||
os = [
|
||||
"nu-utils/os",
|
||||
"os_pipe",
|
||||
]
|
||||
|
||||
plugin = [
|
||||
"brotli",
|
||||
"os",
|
||||
"rmp-serde",
|
||||
]
|
||||
|
||||
|
|
|
@ -1458,6 +1458,17 @@ On Windows, this would be %USERPROFILE%\AppData\Roaming"#
|
|||
#[label = "while running this code"]
|
||||
span: Option<Span>,
|
||||
},
|
||||
|
||||
#[error("OS feature is disabled: {msg}")]
|
||||
#[diagnostic(
|
||||
code(nu::shell::os_disabled),
|
||||
help("You're probably running outside an OS like a browser, we cannot support this")
|
||||
)]
|
||||
DisabledOsSupport {
|
||||
msg: String,
|
||||
#[label = "while running this code"]
|
||||
span: Option<Span>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ShellError {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#![cfg_attr(not(feature = "os"), allow(unused))]
|
||||
#![doc = include_str!("../README.md")]
|
||||
mod alias;
|
||||
pub mod ast;
|
||||
|
@ -17,6 +18,7 @@ pub mod parser_path;
|
|||
mod pipeline;
|
||||
#[cfg(feature = "plugin")]
|
||||
mod plugin;
|
||||
#[cfg(feature = "os")]
|
||||
pub mod process;
|
||||
mod signature;
|
||||
pub mod span;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
//! Module managing the streaming of raw bytes between pipeline elements
|
||||
use crate::{
|
||||
process::{ChildPipe, ChildProcess},
|
||||
ErrSpan, IntoSpanned, PipelineData, ShellError, Signals, Span, Type, Value,
|
||||
};
|
||||
#[cfg(feature = "os")]
|
||||
use crate::process::{ChildPipe, ChildProcess};
|
||||
use crate::{ErrSpan, IntoSpanned, PipelineData, ShellError, Signals, Span, Type, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::OwnedFd;
|
||||
|
@ -24,6 +23,7 @@ use std::{
|
|||
pub enum ByteStreamSource {
|
||||
Read(Box<dyn Read + Send + 'static>),
|
||||
File(File),
|
||||
#[cfg(feature = "os")]
|
||||
Child(Box<ChildProcess>),
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ impl ByteStreamSource {
|
|||
match self {
|
||||
ByteStreamSource::Read(read) => Some(SourceReader::Read(read)),
|
||||
ByteStreamSource::File(file) => Some(SourceReader::File(file)),
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(mut child) => child.stdout.take().map(|stdout| match stdout {
|
||||
ChildPipe::Pipe(pipe) => SourceReader::File(convert_file(pipe)),
|
||||
ChildPipe::Tee(tee) => SourceReader::Read(tee),
|
||||
|
@ -40,9 +41,16 @@ impl ByteStreamSource {
|
|||
}
|
||||
|
||||
/// Source is a `Child` or `File`, rather than `Read`. Currently affects trimming
|
||||
fn is_external(&self) -> bool {
|
||||
#[cfg(feature = "os")]
|
||||
pub fn is_external(&self) -> bool {
|
||||
matches!(self, ByteStreamSource::Child(..))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "os"))]
|
||||
pub fn is_external(&self) -> bool {
|
||||
// without os support we never have externals
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ByteStreamSource {
|
||||
|
@ -50,6 +58,7 @@ impl Debug for ByteStreamSource {
|
|||
match self {
|
||||
ByteStreamSource::Read(_) => f.debug_tuple("Read").field(&"..").finish(),
|
||||
ByteStreamSource::File(file) => f.debug_tuple("File").field(file).finish(),
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(child) => f.debug_tuple("Child").field(child).finish(),
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +256,7 @@ impl ByteStream {
|
|||
///
|
||||
/// The type is implicitly `Unknown`, as it's not typically known whether child processes will
|
||||
/// return text or binary.
|
||||
#[cfg(feature = "os")]
|
||||
pub fn child(child: ChildProcess, span: Span) -> Self {
|
||||
Self::new(
|
||||
ByteStreamSource::Child(Box::new(child)),
|
||||
|
@ -260,6 +270,7 @@ impl ByteStream {
|
|||
///
|
||||
/// The type is implicitly `Unknown`, as it's not typically known whether stdin is text or
|
||||
/// binary.
|
||||
#[cfg(feature = "os")]
|
||||
pub fn stdin(span: Span) -> Result<Self, ShellError> {
|
||||
let stdin = os_pipe::dup_stdin().err_span(span)?;
|
||||
let source = ByteStreamSource::File(convert_file(stdin));
|
||||
|
@ -271,6 +282,14 @@ impl ByteStream {
|
|||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "os"))]
|
||||
pub fn stdin(span: Span) -> Result<Self, ShellError> {
|
||||
Err(ShellError::DisabledOsSupport {
|
||||
msg: "Stdin is not supported".to_string(),
|
||||
span: Some(span),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a [`ByteStream`] from a generator function that writes data to the given buffer
|
||||
/// when called, and returns `Ok(false)` on end of stream.
|
||||
pub fn from_fn(
|
||||
|
@ -432,6 +451,7 @@ impl ByteStream {
|
|||
match self.stream {
|
||||
ByteStreamSource::Read(..) => Err(self),
|
||||
ByteStreamSource::File(file) => Ok(file.into()),
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(child) => {
|
||||
if let ChildProcess {
|
||||
stdout: Some(ChildPipe::Pipe(stdout)),
|
||||
|
@ -453,6 +473,7 @@ impl ByteStream {
|
|||
///
|
||||
/// This will only succeed if the [`ByteStreamSource`] of the [`ByteStream`] is [`Child`](ByteStreamSource::Child).
|
||||
/// All other cases return an `Err` with the original [`ByteStream`] in it.
|
||||
#[cfg(feature = "os")]
|
||||
pub fn into_child(self) -> Result<ChildProcess, Self> {
|
||||
if let ByteStreamSource::Child(child) = self.stream {
|
||||
Ok(*child)
|
||||
|
@ -477,6 +498,7 @@ impl ByteStream {
|
|||
file.read_to_end(&mut buf).err_span(self.span)?;
|
||||
Ok(buf)
|
||||
}
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(child) => child.into_bytes(),
|
||||
}
|
||||
}
|
||||
|
@ -551,6 +573,7 @@ impl ByteStream {
|
|||
Ok(())
|
||||
}
|
||||
ByteStreamSource::File(_) => Ok(()),
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(child) => child.wait(),
|
||||
}
|
||||
}
|
||||
|
@ -575,6 +598,7 @@ impl ByteStream {
|
|||
ByteStreamSource::File(file) => {
|
||||
copy_with_signals(file, dest, span, signals)?;
|
||||
}
|
||||
#[cfg(feature = "os")]
|
||||
ByteStreamSource::Child(mut child) => {
|
||||
// All `OutDest`s except `OutDest::PipeSeparate` will cause `stderr` to be `None`.
|
||||
// Only `save`, `tee`, and `complete` set the stderr `OutDest` to `OutDest::PipeSeparate`,
|
||||
|
|
|
@ -9,8 +9,8 @@ version = "0.100.1"
|
|||
|
||||
[dependencies]
|
||||
nu-parser = { version = "0.100.1", path = "../nu-parser" }
|
||||
nu-protocol = { version = "0.100.1", path = "../nu-protocol" }
|
||||
nu-engine = { version = "0.100.1", path = "../nu-engine" }
|
||||
nu-protocol = { version = "0.100.1", path = "../nu-protocol", default-features = false }
|
||||
nu-engine = { version = "0.100.1", path = "../nu-engine", default-features = false }
|
||||
miette = { workspace = true, features = ["fancy-no-backtrace"] }
|
||||
|
||||
log = "0.4"
|
||||
|
|
|
@ -14,9 +14,9 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.100.1" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
fancy-regex = { workspace = true }
|
||||
|
|
|
@ -14,6 +14,6 @@ bench = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
|
||||
unicode-width = { workspace = true }
|
|
@ -17,6 +17,7 @@ bench = false
|
|||
bench = false
|
||||
|
||||
[dependencies]
|
||||
crossterm = { workspace = true, optional = true }
|
||||
fancy-regex = { workspace = true }
|
||||
lscolors = { workspace = true, default-features = false, features = ["nu-ansi-term"] }
|
||||
log = { workspace = true }
|
||||
|
@ -26,6 +27,10 @@ serde = { workspace = true }
|
|||
sys-locale = "0.3"
|
||||
unicase = "2.8.0"
|
||||
|
||||
[features]
|
||||
default = ["os"]
|
||||
os = ["crossterm"]
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
crossterm_winapi = "0.9"
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[cfg(any(windows, unix))]
|
||||
use std::path::Path;
|
||||
#[cfg(unix)]
|
||||
use {
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use locale::get_system_locale;
|
|||
pub use utils::{
|
||||
enable_vt_processing, get_default_config, get_default_env, get_ls_colors, get_sample_config,
|
||||
get_sample_env, get_scaffold_config, get_scaffold_env, stderr_write_all_and_flush,
|
||||
stdout_write_all_and_flush,
|
||||
stdout_write_all_and_flush, terminal_size,
|
||||
};
|
||||
|
||||
pub use casing::IgnoreCaseExt;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[cfg(windows)]
|
||||
use crossterm_winapi::{ConsoleMode, Handle};
|
||||
use lscolors::LsColors;
|
||||
use std::io::{Result, Write};
|
||||
use std::io::{self, Result, Write};
|
||||
|
||||
pub fn enable_vt_processing() -> Result<()> {
|
||||
#[cfg(windows)]
|
||||
|
@ -474,3 +474,17 @@ macro_rules! perf {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the terminal size (columns, rows).
|
||||
///
|
||||
/// This utility variant allows getting a fallback value when compiling for wasm32 without having
|
||||
/// to rearrange other bits of the codebase.
|
||||
///
|
||||
/// See [`crossterm::terminal::size`].
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
#[cfg(feature = "os")]
|
||||
return crossterm::terminal::size();
|
||||
|
||||
#[cfg(not(feature = "os"))]
|
||||
return Err(io::Error::from(io::ErrorKind::Unsupported));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ name = "nu_plugin_custom_values"
|
|||
bench = false
|
||||
|
||||
[dependencies]
|
||||
nu-plugin = { path = "../nu-plugin", version = "0.100.1" }
|
||||
nu-plugin = { path = "../nu-plugin", version = "0.100.1", features = [] }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", features = ["plugin"] }
|
||||
serde = { workspace = true }
|
||||
typetag = "0.2"
|
||||
|
|
|
@ -11,9 +11,9 @@ version = "0.100.1"
|
|||
|
||||
[dependencies]
|
||||
nu-parser = { path = "../nu-parser", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.100.1", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.100.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.100.1", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
chrono = { workspace = true }
|
||||
|
|
34
toolkit.nu
34
toolkit.nu
|
@ -363,6 +363,40 @@ export def build [
|
|||
}
|
||||
}
|
||||
|
||||
# build crates for wasm
|
||||
export def "build wasm" [] {
|
||||
^rustup target add wasm32-unknown-unknown
|
||||
|
||||
# these crates should compile for wasm
|
||||
let compatible_crates = [
|
||||
"nu-cmd-base",
|
||||
"nu-cmd-extra",
|
||||
"nu-cmd-lang",
|
||||
"nu-color-config",
|
||||
"nu-command",
|
||||
"nu-derive-value",
|
||||
"nu-engine",
|
||||
"nu-glob",
|
||||
"nu-json",
|
||||
"nu-parser",
|
||||
"nu-path",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-std",
|
||||
"nu-system",
|
||||
"nu-table",
|
||||
"nu-term-grid",
|
||||
"nu-utils",
|
||||
"nuon"
|
||||
]
|
||||
|
||||
for crate in $compatible_crates {
|
||||
print $'(char nl)Building ($crate) for wasm'
|
||||
print '----------------------------'
|
||||
^cargo build -p $crate --target wasm32-unknown-unknown --no-default-features
|
||||
}
|
||||
}
|
||||
|
||||
def "nu-complete list features" [] {
|
||||
open Cargo.toml | get features | transpose feature dependencies | get feature
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue