mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 06:58:12 +08:00
Replace the dev server's wasm-pack, cargo-watch, and concurrently tools with custom cargo-run tooling (#4254)
Replace wasm-pack, cargo-watch and concurrently with custom build tooling Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
1f05000238
commit
a72ac39059
@@ -6,7 +6,7 @@
|
|||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {}
|
"ghcr.io/devcontainers/features/node:1": {}
|
||||||
},
|
},
|
||||||
"onCreateCommand": "cargo install cargo-watch wasm-pack cargo-about && cargo install -f wasm-bindgen-cli@0.2.121",
|
"onCreateCommand": "cargo install cargo-about && cargo install -f wasm-bindgen-cli@0.2.121",
|
||||||
"customizations": {
|
"customizations": {
|
||||||
"vscode": {
|
"vscode": {
|
||||||
// NOTE: Keep this in sync with `.vscode/extensions.json`
|
// NOTE: Keep this in sync with `.vscode/extensions.json`
|
||||||
|
|||||||
+28
-12
@@ -58,6 +58,8 @@ jobs:
|
|||||||
RUSTC_WRAPPER: /usr/bin/sccache
|
RUSTC_WRAPPER: /usr/bin/sccache
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
SCCACHE_DIR: /var/lib/github-actions/.cache
|
SCCACHE_DIR: /var/lib/github-actions/.cache
|
||||||
|
WASM_BINDGEN_CLI_VERSION: "0.2.121"
|
||||||
|
BINARYEN_VERSION: "130"
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
@@ -65,19 +67,11 @@ jobs:
|
|||||||
repository: ${{ inputs.checkout_repo || github.repository }}
|
repository: ${{ inputs.checkout_repo || github.repository }}
|
||||||
ref: ${{ inputs.checkout_ref || '' }}
|
ref: ${{ inputs.checkout_ref || '' }}
|
||||||
|
|
||||||
- name: 🗑 Clear wasm-bindgen cache
|
|
||||||
run: rm -r ~/.cache/.wasm-pack || true
|
|
||||||
|
|
||||||
- name: 🟢 Install Node.js
|
- name: 🟢 Install Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version-file: .nvmrc
|
node-version-file: .nvmrc
|
||||||
|
|
||||||
- name: 🚧 Install build dependencies
|
|
||||||
run: |
|
|
||||||
cd frontend
|
|
||||||
npm run setup
|
|
||||||
|
|
||||||
- name: 🦀 Install Rust
|
- name: 🦀 Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
@@ -87,6 +81,19 @@ jobs:
|
|||||||
rustflags: ""
|
rustflags: ""
|
||||||
target: wasm32-unknown-unknown
|
target: wasm32-unknown-unknown
|
||||||
|
|
||||||
|
- name: 🚧 Install wasm-bindgen-cli and Binaryen wasm-opt
|
||||||
|
run: |
|
||||||
|
if ! wasm-bindgen --version 2>/dev/null | grep -qF "$WASM_BINDGEN_CLI_VERSION"; then
|
||||||
|
cargo install -f "wasm-bindgen-cli@$WASM_BINDGEN_CLI_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
BINARYEN_DIR="$HOME/.cache/binaryen-version_$BINARYEN_VERSION"
|
||||||
|
if [ ! -x "$BINARYEN_DIR/bin/wasm-opt" ]; then
|
||||||
|
mkdir -p "$BINARYEN_DIR"
|
||||||
|
curl -sSfL "https://github.com/WebAssembly/binaryen/releases/download/version_$BINARYEN_VERSION/binaryen-version_$BINARYEN_VERSION-x86_64-linux.tar.gz" | tar xz -C "$BINARYEN_DIR" --strip-components=1
|
||||||
|
fi
|
||||||
|
echo "$BINARYEN_DIR/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: 🔀 Choose production deployment environment and insert template
|
- name: 🔀 Choose production deployment environment and insert template
|
||||||
id: production-env
|
id: production-env
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
@@ -294,6 +301,7 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
WASM_BINDGEN_CLI_VERSION: "0.2.121"
|
WASM_BINDGEN_CLI_VERSION: "0.2.121"
|
||||||
|
BINARYEN_VERSION: "130"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Clone repository
|
- name: 📥 Clone repository
|
||||||
@@ -341,13 +349,15 @@ jobs:
|
|||||||
winget install --id LLVM.LLVM -e --accept-package-agreements --accept-source-agreements
|
winget install --id LLVM.LLVM -e --accept-package-agreements --accept-source-agreements
|
||||||
winget install --id Kitware.CMake -e --accept-package-agreements --accept-source-agreements
|
winget install --id Kitware.CMake -e --accept-package-agreements --accept-source-agreements
|
||||||
winget install --id OpenSSL.OpenSSL -e --accept-package-agreements --accept-source-agreements
|
winget install --id OpenSSL.OpenSSL -e --accept-package-agreements --accept-source-agreements
|
||||||
winget install --id WebAssembly.Binaryen -e --accept-package-agreements --accept-source-agreements
|
|
||||||
winget install --id GnuWin32.PkgConfig -e --accept-package-agreements --accept-source-agreements
|
winget install --id GnuWin32.PkgConfig -e --accept-package-agreements --accept-source-agreements
|
||||||
|
|
||||||
"OPENSSL_DIR=C:\Program Files\OpenSSL-Win64" | Out-File -FilePath $env:GITHUB_ENV -Append
|
"OPENSSL_DIR=C:\Program Files\OpenSSL-Win64" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
"PKG_CONFIG_PATH=C:\Program Files\OpenSSL-Win64\lib\pkgconfig" | Out-File -FilePath $env:GITHUB_ENV -Append
|
"PKG_CONFIG_PATH=C:\Program Files\OpenSSL-Win64\lib\pkgconfig" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||||
|
|
||||||
cargo binstall --no-confirm --force wasm-pack
|
curl.exe -sSfL -o "$env:RUNNER_TEMP\binaryen.tar.gz" "https://github.com/WebAssembly/binaryen/releases/download/version_$env:BINARYEN_VERSION/binaryen-version_$env:BINARYEN_VERSION-x86_64-windows.tar.gz"
|
||||||
|
tar -xzf "$env:RUNNER_TEMP\binaryen.tar.gz" -C $env:RUNNER_TEMP
|
||||||
|
"$env:RUNNER_TEMP\binaryen-version_$env:BINARYEN_VERSION\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
|
||||||
|
|
||||||
cargo binstall --no-confirm --force cargo-about
|
cargo binstall --no-confirm --force cargo-about
|
||||||
cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION"
|
cargo binstall --no-confirm --force "wasm-bindgen-cli@$env:WASM_BINDGEN_CLI_VERSION"
|
||||||
|
|
||||||
@@ -485,6 +495,7 @@ jobs:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
WASM_BINDGEN_CLI_VERSION: "0.2.121"
|
WASM_BINDGEN_CLI_VERSION: "0.2.121"
|
||||||
|
BINARYEN_VERSION: "130"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Clone repository
|
- name: 📥 Clone repository
|
||||||
@@ -529,7 +540,6 @@ jobs:
|
|||||||
brew install \
|
brew install \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
openssl@3 \
|
openssl@3 \
|
||||||
binaryen \
|
|
||||||
llvm \
|
llvm \
|
||||||
cargo-binstall
|
cargo-binstall
|
||||||
|
|
||||||
@@ -537,7 +547,13 @@ jobs:
|
|||||||
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
|
echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV
|
||||||
echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH
|
echo "$(brew --prefix llvm)/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
cargo binstall --no-confirm --force wasm-pack
|
BINARYEN_DIR="$HOME/.cache/binaryen-version_$BINARYEN_VERSION"
|
||||||
|
if [ ! -x "$BINARYEN_DIR/bin/wasm-opt" ]; then
|
||||||
|
mkdir -p "$BINARYEN_DIR"
|
||||||
|
curl -sSfL "https://github.com/WebAssembly/binaryen/releases/download/version_$BINARYEN_VERSION/binaryen-version_$BINARYEN_VERSION-arm64-macos.tar.gz" | tar xz -C "$BINARYEN_DIR" --strip-components=1
|
||||||
|
fi
|
||||||
|
echo "$BINARYEN_DIR/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
cargo binstall --no-confirm --force cargo-about
|
cargo binstall --no-confirm --force cargo-about
|
||||||
cargo binstall --no-confirm --force "wasm-bindgen-cli@${WASM_BINDGEN_CLI_VERSION}"
|
cargo binstall --no-confirm --force "wasm-bindgen-cli@${WASM_BINDGEN_CLI_VERSION}"
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ pkgs.mkShell (
|
|||||||
pkgs.nodejs
|
pkgs.nodejs
|
||||||
pkgs.binaryen
|
pkgs.binaryen
|
||||||
pkgs.wasm-bindgen-cli_0_2_121
|
pkgs.wasm-bindgen-cli_0_2_121
|
||||||
pkgs.wasm-pack
|
|
||||||
pkgs.cargo-about
|
pkgs.cargo-about
|
||||||
|
|
||||||
pkgs.rustc
|
pkgs.rustc
|
||||||
@@ -41,7 +40,6 @@ pkgs.mkShell (
|
|||||||
|
|
||||||
pkgs.git
|
pkgs.git
|
||||||
|
|
||||||
pkgs.cargo-watch
|
|
||||||
pkgs.cargo-nextest
|
pkgs.cargo-nextest
|
||||||
pkgs.cargo-expand
|
pkgs.cargo-expand
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,6 @@ deps.crane.lib.buildPackage (
|
|||||||
pkgs.nodejs
|
pkgs.nodejs
|
||||||
pkgs.binaryen
|
pkgs.binaryen
|
||||||
pkgs.wasm-bindgen-cli_0_2_121
|
pkgs.wasm-bindgen-cli_0_2_121
|
||||||
pkgs.wasm-pack
|
|
||||||
pkgs.cargo-about
|
pkgs.cargo-about
|
||||||
pkgs.removeReferencesTo
|
pkgs.removeReferencesTo
|
||||||
pkgs.importNpmLock.npmConfigHook
|
pkgs.importNpmLock.npmConfigHook
|
||||||
@@ -94,7 +93,6 @@ deps.crane.lib.buildPackage (
|
|||||||
npmRoot = "${info.src}/frontend";
|
npmRoot = "${info.src}/frontend";
|
||||||
};
|
};
|
||||||
npmRoot = "frontend";
|
npmRoot = "frontend";
|
||||||
npmConfigScript = "setup";
|
|
||||||
makeCacheWritable = true;
|
makeCacheWritable = true;
|
||||||
|
|
||||||
env = {
|
env = {
|
||||||
|
|||||||
Generated
+180
@@ -505,9 +505,20 @@ dependencies = [
|
|||||||
name = "cargo-run"
|
name = "cargo-run"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"duct",
|
||||||
|
"semver",
|
||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-run-internal-watch"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"cargo-run",
|
||||||
|
"notify",
|
||||||
|
"notify-debouncer-full",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cargo-util-schemas"
|
name = "cargo-util-schemas"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -1335,6 +1346,18 @@ dependencies = [
|
|||||||
"dtoa",
|
"dtoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "duct"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e66e9c0c03d094e1a0ba1be130b849034aa80c3a2ab8ee94316bc809f3fa684"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"os_pipe",
|
||||||
|
"shared_child",
|
||||||
|
"shared_thread",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dunce"
|
name = "dunce"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -1514,6 +1537,15 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "file-id"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1fc6a637b6dc58414714eddd9170ff187ecb0933d4c7024d1abbd23a3cc26e9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.26"
|
version = "0.2.26"
|
||||||
@@ -1637,6 +1669,15 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futf"
|
name = "futf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -2797,6 +2838,26 @@ dependencies = [
|
|||||||
"rustversion",
|
"rustversion",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interpreted-executor"
|
name = "interpreted-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2992,6 +3053,26 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5"
|
||||||
|
dependencies = [
|
||||||
|
"kqueue-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kqueue-sys"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.13.1"
|
version = "0.13.1"
|
||||||
@@ -3257,6 +3338,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -3433,6 +3515,46 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"fsevent-sys",
|
||||||
|
"inotify",
|
||||||
|
"kqueue",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"notify-types",
|
||||||
|
"walkdir",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify-debouncer-full"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c02b49179cfebc9932238d04d6079912d26de0379328872846118a0fa0dbb302"
|
||||||
|
dependencies = [
|
||||||
|
"file-id",
|
||||||
|
"log",
|
||||||
|
"notify",
|
||||||
|
"notify-types",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify-types"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.50.1"
|
version = "0.50.1"
|
||||||
@@ -3807,6 +3929,16 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_pipe"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owned_ttf_parser"
|
name = "owned_ttf_parser"
|
||||||
version = "0.25.1"
|
version = "0.25.1"
|
||||||
@@ -5207,12 +5339,60 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared_child"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"sigchld",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shared_thread"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52b86057fcb5423f5018e331ac04623e32d6b5ce85e33300f92c79a1973928b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sigchld"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"os_pipe",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
|
||||||
|
dependencies = [
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simd-adler32"
|
name = "simd-adler32"
|
||||||
version = "0.3.7"
|
version = "0.3.7"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ members = [
|
|||||||
"node-graph/preprocessor",
|
"node-graph/preprocessor",
|
||||||
"proc-macros",
|
"proc-macros",
|
||||||
"tools/cargo-run",
|
"tools/cargo-run",
|
||||||
|
"tools/cargo-run/internal/*",
|
||||||
"tools/crate-hierarchy-viz",
|
"tools/crate-hierarchy-viz",
|
||||||
"tools/third-party-licenses",
|
"tools/third-party-licenses",
|
||||||
"tools/editor-message-tree",
|
"tools/editor-message-tree",
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
wasm/pkg/
|
wrapper/pkg/
|
||||||
public/build/
|
public/build/
|
||||||
dist/
|
dist/
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ Source code for the web app in the form of Svelte components and [TypeScript](ht
|
|||||||
|
|
||||||
## Editor wrapper: `wrapper/`
|
## Editor wrapper: `wrapper/`
|
||||||
|
|
||||||
Wraps the editor backend codebase (`/editor`) and provides a JS-centric API for the web app to use as an entry point, unburdened by Rust's complex data types that are incompatible with JS data types. Bindings (JS functions that call into the Wasm module) are provided by [wasm-bindgen](https://rustwasm.github.io/docs/wasm-bindgen/) in concert with [wasm-pack](https://github.com/rustwasm/wasm-pack).
|
Wraps the editor backend codebase (`/editor`) and provides a JS-centric API for the web app to use as an entry point, unburdened by Rust's complex data types that are incompatible with JS data types. Bindings (JS functions that call into the Wasm module) are provided by `wasm-bindgen`. As part of `cargo run`, our build tool compiles this crate to Wasm, runs the `wasm-bindgen` CLI to generate the JS/TS bindings in `wrapper/pkg/`, and (for release builds) optimizes the binary with Binaryen's `wasm-opt`.
|
||||||
|
|
||||||
## ESLint configuration: `eslint.config.js`
|
## ESLint configuration: `eslint.config.js`
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// This script automatically installs the npm packages listed in package-lock.json and runs before `npm start`.
|
// This script automatically installs the npm packages listed in package-lock.json and runs as part of `npm run setup` (invoked by `cargo run`).
|
||||||
// It skips the installation if this has already run and neither package.json nor package-lock.json has been modified since.
|
// It skips the installation if this has already run and neither package.json nor package-lock.json has been modified since.
|
||||||
|
|
||||||
import { execSync } from "child_process";
|
import { execSync } from "child_process";
|
||||||
@@ -11,7 +11,7 @@ const isInstallNeeded = () => {
|
|||||||
if (!existsSync(INSTALL_TIMESTAMP_FILE)) return true;
|
if (!existsSync(INSTALL_TIMESTAMP_FILE)) return true;
|
||||||
|
|
||||||
const timestamp = statSync(INSTALL_TIMESTAMP_FILE).mtime;
|
const timestamp = statSync(INSTALL_TIMESTAMP_FILE).mtime;
|
||||||
return ["package.json", "package-lock.json"].some((file) => {
|
return ["package.json", "package-lock.json", "package-installer.js"].some((file) => {
|
||||||
return existsSync(file) && statSync(file).mtime > timestamp;
|
return existsSync(file) && statSync(file).mtime > timestamp;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -22,8 +22,10 @@ if (isInstallNeeded()) {
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("Installing npm packages...");
|
console.log("Installing npm packages...");
|
||||||
|
|
||||||
// Check if packages are up to date, doing so quickly by using `npm ci`, preferring local cached packages, and skipping the package audit and other checks
|
// Check if packages are up to date, doing so quickly by using `npm ci`, preferring local cached packages, and skipping the package audit and other checks.
|
||||||
execSync("npm ci --prefer-offline --no-audit --no-fund", { stdio: "inherit" });
|
// The devDependencies are explicitly included because they hold the build tooling (Vite, etc.), which npm would
|
||||||
|
// otherwise omit in environments that set NODE_ENV=production (like CI does for the sake of the Vite build).
|
||||||
|
execSync("npm ci --include=dev --prefer-offline --no-audit --no-fund", { stdio: "inherit" });
|
||||||
|
|
||||||
// Touch the install timestamp file
|
// Touch the install timestamp file
|
||||||
writeFileSync(INSTALL_TIMESTAMP_FILE, "");
|
writeFileSync(INSTALL_TIMESTAMP_FILE, "");
|
||||||
|
|||||||
Generated
+2
-268
@@ -14,7 +14,6 @@
|
|||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"concurrently": "^9.2.1",
|
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
@@ -2069,84 +2068,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cliui": {
|
|
||||||
"version": "8.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
|
||||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"strip-ansi": "^6.0.1",
|
|
||||||
"wrap-ansi": "^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cliui/node_modules/wrap-ansi": {
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-styles": "^4.0.0",
|
|
||||||
"string-width": "^4.1.0",
|
|
||||||
"strip-ansi": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
@@ -2184,31 +2105,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/concurrently": {
|
|
||||||
"version": "9.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
|
|
||||||
"integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"chalk": "4.1.2",
|
|
||||||
"rxjs": "7.8.2",
|
|
||||||
"shell-quote": "1.8.3",
|
|
||||||
"supports-color": "8.1.1",
|
|
||||||
"tree-kill": "1.2.2",
|
|
||||||
"yargs": "17.7.2"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"conc": "dist/bin/concurrently.js",
|
|
||||||
"concurrently": "dist/bin/concurrently.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
@@ -2570,16 +2466,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/escalade": {
|
|
||||||
"version": "3.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
|
||||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@@ -3206,16 +3092,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/get-caller-file": {
|
|
||||||
"version": "2.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": "6.* || 8.* || >= 10.*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
@@ -5270,16 +5146,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/require-directory": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "2.0.0-next.6",
|
"version": "2.0.0-next.6",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.6.tgz",
|
||||||
@@ -5358,16 +5224,6 @@
|
|||||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12"
|
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rxjs": {
|
|
||||||
"version": "7.8.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
|
|
||||||
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/sade": {
|
"node_modules/sade": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
|
||||||
@@ -5539,19 +5395,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/shell-quote": {
|
|
||||||
"version": "1.8.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
|
||||||
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
@@ -5951,22 +5794,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/supports-color": {
|
|
||||||
"version": "8.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
|
||||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"has-flag": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/supports-preserve-symlinks-flag": {
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
@@ -6126,16 +5953,6 @@
|
|||||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tree-kill": {
|
|
||||||
"version": "1.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
|
||||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"tree-kill": "cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/treeify": {
|
"node_modules/treeify": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
|
||||||
@@ -6177,7 +5994,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -6705,16 +6523,6 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/y18n": {
|
|
||||||
"version": "5.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
|
||||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
||||||
@@ -6725,80 +6533,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yargs": {
|
|
||||||
"version": "17.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
|
||||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cliui": "^8.0.1",
|
|
||||||
"escalade": "^3.1.1",
|
|
||||||
"get-caller-file": "^2.0.5",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"string-width": "^4.2.3",
|
|
||||||
"y18n": "^5.0.5",
|
|
||||||
"yargs-parser": "^21.1.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yargs-parser": {
|
|
||||||
"version": "21.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yargs/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yargs/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/yargs/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yargs/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
|
|||||||
+1
-18
@@ -6,25 +6,9 @@
|
|||||||
"browserslist": "> 1.5%, last 2 versions, not dead, not ie 11, not op_mini all, not ios_saf < 13",
|
"browserslist": "> 1.5%, last 2 versions, not dead, not ie 11, not op_mini all, not ios_saf < 13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"---------- DEV SERVER ----------": "",
|
|
||||||
"start": "npm run setup && npm run wasm:build-dev && concurrently -k -n \"VITE,RUST\" \"vite\" \"npm run wasm:watch-dev\"",
|
|
||||||
"production": "npm run setup && npm run wasm:build-production && concurrently -k -n \"VITE,RUST\" \"vite\" \"npm run wasm:watch-production\"",
|
|
||||||
"---------- BUILDS ----------": "",
|
|
||||||
"build": "npm run setup && npm run wasm:build-production && vite build",
|
|
||||||
"build-dev": "npm run setup && npm run wasm:build-dev && vite build --mode dev",
|
|
||||||
"build-native": "npm run setup && npm run native:build-production",
|
|
||||||
"build-native-dev": "npm run setup && npm run native:build-dev",
|
|
||||||
"---------- UTILITIES ----------": "",
|
|
||||||
"check": "svelte-check --fail-on-warnings && eslint",
|
"check": "svelte-check --fail-on-warnings && eslint",
|
||||||
"fix": "eslint --fix",
|
"fix": "eslint --fix",
|
||||||
"---------- INTERNAL ----------": "",
|
"setup": "node package-installer.js && node branding-installer.js"
|
||||||
"setup": "node package-installer.js && node branding-installer.js",
|
|
||||||
"native:build-dev": "wasm-pack build ./wrapper --dev --target=web --no-default-features --features native && vite build --mode native",
|
|
||||||
"native:build-production": "wasm-pack build ./wrapper --release --target=web --no-default-features --features native && vite build --mode native",
|
|
||||||
"wasm:build-dev": "wasm-pack build ./wrapper --dev --target=web",
|
|
||||||
"wasm:build-production": "wasm-pack build ./wrapper --release --target=web",
|
|
||||||
"wasm:watch-dev": "cargo watch --postpone --watch-when-idle --workdir=wrapper --shell \"wasm-pack build . --dev --target=web -- --color=always\"",
|
|
||||||
"wasm:watch-production": "cargo watch --postpone --watch-when-idle --workdir=wrapper --shell \"wasm-pack build . --release --target=web -- --color=always\""
|
|
||||||
},
|
},
|
||||||
"//": "NOTE: `source-sans-pro` is never to be upgraded to 3.x because that renders 1px above its intended position.",
|
"//": "NOTE: `source-sans-pro` is never to be upgraded to 3.x because that renders 1px above its intended position.",
|
||||||
"///": "Waiting on <https://github.com/import-js/eslint-plugin-import/issues/3227> before we can update @eslint/js and eslint to 10.x",
|
"///": "Waiting on <https://github.com/import-js/eslint-plugin-import/issues/3227> before we can update @eslint/js and eslint to 10.x",
|
||||||
@@ -36,7 +20,6 @@
|
|||||||
"@eslint/js": "^9.39.2",
|
"@eslint/js": "^9.39.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@types/node": "^25.5.2",
|
"@types/node": "^25.5.2",
|
||||||
"concurrently": "^9.2.1",
|
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-import-resolver-typescript": "^4.4.4",
|
"eslint-import-resolver-typescript": "^4.4.4",
|
||||||
"eslint-plugin-import": "^2.32.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
|
|||||||
@@ -40,22 +40,6 @@ ron = { workspace = true }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
node-macro = { workspace = true }
|
node-macro = { workspace = true }
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.dev]
|
|
||||||
wasm-opt = false
|
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.dev.wasm-bindgen]
|
|
||||||
debug-js-glue = true
|
|
||||||
demangle-name-section = true
|
|
||||||
dwarf-debug-info = false
|
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.release]
|
|
||||||
wasm-opt = ["-Os", "-g"]
|
|
||||||
|
|
||||||
[package.metadata.wasm-pack.profile.release.wasm-bindgen]
|
|
||||||
debug-js-glue = false
|
|
||||||
demangle-name-section = false
|
|
||||||
dwarf-debug-info = false
|
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||||
'cfg(wasm_bindgen_unstable_test_coverage)',
|
'cfg(wasm_bindgen_unstable_test_coverage)',
|
||||||
|
|||||||
@@ -9,3 +9,5 @@ default-run = "cargo-run"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
semver = "1"
|
||||||
|
duct = "1"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "cargo-run-internal-watch"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cargo-run = { path = "../.." }
|
||||||
|
notify = "8.2.0"
|
||||||
|
notify-debouncer-full = "0.7.0"
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
use cargo_run::Error;
|
||||||
|
use cargo_run::cmd::prelude::*;
|
||||||
|
use notify::RecursiveMode;
|
||||||
|
use notify::event::{EventKind, ModifyKind};
|
||||||
|
use notify_debouncer_full::{Debouncer, RecommendedCache, new_debouncer, notify::RecommendedWatcher};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::ExitCode;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
const EXCLUDED_DIRECTORIES: &[&str] = &["target", ".git", "frontend/node_modules", "frontend/dist", "frontend/wrapper/pkg"];
|
||||||
|
const INCLUDED_EXTENSIONS: &[&str] = &["rs"];
|
||||||
|
|
||||||
|
const DEBOUNCE: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let release = std::env::args().nth(1).as_deref() == Some("release");
|
||||||
|
|
||||||
|
let _guard = match watch(cargo_run::frontend::build_wasm_steps(release, false)) {
|
||||||
|
Ok(guard) => guard,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error setting up file watcher: {e}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("Watching for changes...");
|
||||||
|
loop {
|
||||||
|
std::thread::park();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WatchGuard {
|
||||||
|
_debouncer: Debouncer<RecommendedWatcher, RecommendedCache>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn watch(steps: impl IntoIterator<Item = Expression>) -> Result<WatchGuard, Error> {
|
||||||
|
let steps: Vec<Expression> = steps.into_iter().collect();
|
||||||
|
let root = std::env::current_dir()
|
||||||
|
.and_then(|p| p.canonicalize())
|
||||||
|
.map_err(|e| Error::Io(e, "Failed to resolve root for file watcher".into()))?;
|
||||||
|
let root_clone = root.clone();
|
||||||
|
|
||||||
|
let mut current: Option<Sequence> = None;
|
||||||
|
|
||||||
|
let mut debouncer = new_debouncer(DEBOUNCE, None, move |result: notify_debouncer_full::DebounceEventResult| match result {
|
||||||
|
Ok(events) => {
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
let mut triggered = false;
|
||||||
|
for ev in events {
|
||||||
|
if !matches!(
|
||||||
|
&ev.event.kind,
|
||||||
|
EventKind::Create(_) | EventKind::Remove(_) | EventKind::Modify(ModifyKind::Data(_) | ModifyKind::Name(_) | ModifyKind::Any)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for path in &ev.event.paths {
|
||||||
|
if is_excluded(path, &root) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if seen.insert(path.clone()) {
|
||||||
|
triggered = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if triggered {
|
||||||
|
if let Some(c) = current.take() {
|
||||||
|
c.kill();
|
||||||
|
}
|
||||||
|
current = Some(sequence(steps.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(errors) => {
|
||||||
|
for e in errors {
|
||||||
|
eprintln!("watch: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map_err(|e| Error::Io(std::io::Error::other(e.to_string()), "file watcher".into()))?;
|
||||||
|
|
||||||
|
debouncer
|
||||||
|
.watch(&root_clone, RecursiveMode::Recursive)
|
||||||
|
.map_err(|e| Error::Io(std::io::Error::other(e.to_string()), "file watcher".into()))?;
|
||||||
|
|
||||||
|
Ok(WatchGuard { _debouncer: debouncer })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_excluded(path: &Path, root: &Path) -> bool {
|
||||||
|
let rel = path.strip_prefix(root).unwrap_or(path);
|
||||||
|
if EXCLUDED_DIRECTORIES.iter().any(|d| rel.starts_with(d)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
!path.extension().and_then(|e| e.to_str()).is_some_and(|e| INCLUDED_EXTENSIONS.contains(&e))
|
||||||
|
}
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
use crate::Error;
|
||||||
|
use duct::{Handle, ReaderHandle};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::thread::JoinHandle;
|
||||||
|
|
||||||
|
pub use duct::{Expression, cmd};
|
||||||
|
|
||||||
|
pub mod prelude {
|
||||||
|
pub use super::{Expression, ExpressionExt, Sequence, TerminalColor, cmd, sequence, supervise, utils};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ExpressionExt {
|
||||||
|
fn arg(self, arg: impl Into<OsString>) -> Self;
|
||||||
|
fn args<I, S>(self, args: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>;
|
||||||
|
fn arg_if(self, cond: bool, arg: impl Into<OsString>) -> Self;
|
||||||
|
fn args_if<I, S>(self, cond: bool, args: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>;
|
||||||
|
fn run(self) -> Result<(), Error>;
|
||||||
|
fn read(self) -> Result<String, Error>;
|
||||||
|
fn output_unchecked(self) -> Result<std::process::Output, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExpressionExt for Expression {
|
||||||
|
fn arg(self, arg: impl Into<OsString>) -> Self {
|
||||||
|
let arg = arg.into();
|
||||||
|
self.before_spawn(move |c| {
|
||||||
|
c.arg(&arg);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args<I, S>(self, args: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>,
|
||||||
|
{
|
||||||
|
let args: Vec<OsString> = args.into_iter().map(Into::into).collect();
|
||||||
|
self.before_spawn(move |c| {
|
||||||
|
c.args(&args);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_if(self, cond: bool, arg: impl Into<OsString>) -> Self {
|
||||||
|
if cond { self.arg(arg) } else { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn args_if<I, S>(self, cond: bool, args: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>,
|
||||||
|
{
|
||||||
|
if cond { self.args(args) } else { self }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Result<(), Error> {
|
||||||
|
Expression::run(&self).map_err(Error::Command)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(self) -> Result<String, Error> {
|
||||||
|
Expression::read(&self).map_err(Error::Command)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_unchecked(self) -> Result<std::process::Output, Error> {
|
||||||
|
let e = self.unchecked().stdout_capture().stderr_capture();
|
||||||
|
Expression::run(&e).map_err(Error::Command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum TerminalColor {
|
||||||
|
Black,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Magenta,
|
||||||
|
Cyan,
|
||||||
|
White,
|
||||||
|
Reset,
|
||||||
|
}
|
||||||
|
impl TerminalColor {
|
||||||
|
fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Black => "\x1b[30m",
|
||||||
|
Self::Red => "\x1b[31m",
|
||||||
|
Self::Green => "\x1b[32m",
|
||||||
|
Self::Yellow => "\x1b[33m",
|
||||||
|
Self::Blue => "\x1b[34m",
|
||||||
|
Self::Magenta => "\x1b[35m",
|
||||||
|
Self::Cyan => "\x1b[36m",
|
||||||
|
Self::White => "\x1b[37m",
|
||||||
|
Self::Reset => "\x1b[0m",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sequence<I: IntoIterator<Item = Expression>>(expressions: I) -> Sequence {
|
||||||
|
let expressions: Vec<Expression> = expressions.into_iter().collect();
|
||||||
|
let current: Arc<Mutex<Option<Arc<Handle>>>> = Arc::new(Mutex::new(None));
|
||||||
|
let killed = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let worker_current = Arc::clone(¤t);
|
||||||
|
let worker_killed = Arc::clone(&killed);
|
||||||
|
let worker = std::thread::spawn(move || {
|
||||||
|
for expr in expressions {
|
||||||
|
if worker_killed.load(Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let handle = match expr.start() {
|
||||||
|
Ok(h) => Arc::new(h),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("sequence: failed to start step: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
{
|
||||||
|
let mut slot = worker_current.lock().unwrap();
|
||||||
|
if worker_killed.load(Ordering::SeqCst) {
|
||||||
|
let _ = handle.kill();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*slot = Some(Arc::clone(&handle));
|
||||||
|
}
|
||||||
|
let result = handle.wait().map(|_| ());
|
||||||
|
worker_current.lock().unwrap().take();
|
||||||
|
if worker_killed.load(Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(e) = result {
|
||||||
|
eprintln!("sequence: step failed: {e}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Sequence {
|
||||||
|
current,
|
||||||
|
killed,
|
||||||
|
worker: Some(worker),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sequence {
|
||||||
|
current: Arc<Mutex<Option<Arc<Handle>>>>,
|
||||||
|
killed: Arc<AtomicBool>,
|
||||||
|
worker: Option<JoinHandle<()>>,
|
||||||
|
}
|
||||||
|
impl Sequence {
|
||||||
|
pub fn kill(&self) {
|
||||||
|
let slot = self.current.lock().unwrap();
|
||||||
|
self.killed.store(true, Ordering::SeqCst);
|
||||||
|
if let Some(handle) = slot.as_ref() {
|
||||||
|
let _ = handle.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(&mut self) {
|
||||||
|
if let Some(w) = self.worker.take() {
|
||||||
|
let _ = w.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for Sequence {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.kill();
|
||||||
|
self.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn supervise<I, S>(children: I) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (S, TerminalColor, Expression)>,
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
use std::io::{BufRead, BufReader, IsTerminal, Write};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
windows_ctrl_c::install();
|
||||||
|
|
||||||
|
let mut handles: Vec<(String, TerminalColor, Arc<ReaderHandle>)> = Vec::new();
|
||||||
|
for (label, color, expr) in children {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let expr = expr.before_spawn(|cmd| {
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
const CREATE_NEW_PROCESS_GROUP: u32 = 0x0000_0200;
|
||||||
|
cmd.creation_flags(CREATE_NEW_PROCESS_GROUP);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let handle = expr.stderr_to_stdout().reader().map_err(Error::Command)?;
|
||||||
|
handles.push((label.into(), color, Arc::new(handle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut io_threads = Vec::new();
|
||||||
|
for (label, color, handle) in handles.iter() {
|
||||||
|
let prefix = if std::io::stdout().is_terminal() {
|
||||||
|
format!("{color}[{label}]{reset} ", color = color.as_str(), reset = TerminalColor::Reset.as_str())
|
||||||
|
} else {
|
||||||
|
format!("[{label}] ")
|
||||||
|
};
|
||||||
|
let handle = handle.clone();
|
||||||
|
io_threads.push(thread::spawn(move || {
|
||||||
|
let reader = BufReader::new(&*handle);
|
||||||
|
for line in reader.lines().map_while(Result::ok) {
|
||||||
|
let out = std::io::stdout();
|
||||||
|
let mut out = out.lock();
|
||||||
|
let _ = writeln!(out, "{prefix}{line}");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reason: Option<(String, std::process::ExitStatus)> = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if windows_ctrl_c::interrupted() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (label, _color, handle) in handles.iter() {
|
||||||
|
if let Ok(Some(output)) = handle.try_wait() {
|
||||||
|
reason = Some((label.clone(), output.status));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reason.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_millis(200));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, _color, handle) in handles.iter() {
|
||||||
|
if matches!(handle.try_wait(), Ok(Some(_))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
for pid in handle.pids() {
|
||||||
|
let _ = std::process::Command::new("taskkill")
|
||||||
|
.args(["/T", "/F", "/PID", &pid.to_string()])
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
let _ = handle.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for t in io_threads {
|
||||||
|
let _ = t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
match reason {
|
||||||
|
Some((label, status)) if !status.success() => Err(Error::Command(std::io::Error::other(format!("supervised child '{label}' exited with status {status}")))),
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod utils {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn internal(name: &str) -> Expression {
|
||||||
|
let package = format!("cargo-run-internal-{name}");
|
||||||
|
cmd!("cargo", "run", "-p", package, "--")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn npm<I, S>(args: I) -> Expression
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: Into<OsString>,
|
||||||
|
{
|
||||||
|
let prog = if cfg!(target_os = "windows") { "npm.cmd" } else { "npm" };
|
||||||
|
cmd(prog, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_bin(rel: &str) -> Expression {
|
||||||
|
cmd!("node", format!("node_modules/{rel}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_url(url: &str) -> Result<(), Error> {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let expr = cmd!("cmd", "/c", "start", url);
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let expr = cmd!("open", url);
|
||||||
|
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
||||||
|
let expr = cmd!("xdg-open", url);
|
||||||
|
expr.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod windows_ctrl_c {
|
||||||
|
use std::sync::Once;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
static INTERRUPTED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
unsafe extern "system" {
|
||||||
|
fn SetConsoleCtrlHandler(handler: Option<unsafe extern "system" fn(u32) -> i32>, add: i32) -> i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "system" fn handler(_ctrl_type: u32) -> i32 {
|
||||||
|
INTERRUPTED.store(true, Ordering::SeqCst);
|
||||||
|
1 // Report the event as handled
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install() {
|
||||||
|
INTERRUPTED.store(false, Ordering::SeqCst);
|
||||||
|
static REGISTER: Once = Once::new();
|
||||||
|
REGISTER.call_once(|| unsafe {
|
||||||
|
SetConsoleCtrlHandler(Some(handler), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupted() -> bool {
|
||||||
|
INTERRUPTED.load(Ordering::SeqCst)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use crate::cmd::prelude::*;
|
||||||
|
use crate::*;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
const WRAPPER_CRATE: &str = "graphite-wasm-wrapper";
|
||||||
|
const WASM_TARGET: &str = "wasm32-unknown-unknown";
|
||||||
|
const OUT_NAME: &str = "graphite_wasm_wrapper";
|
||||||
|
|
||||||
|
pub fn frontend_dir() -> PathBuf {
|
||||||
|
workspace_dir().join("frontend")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup() -> Result<(), Error> {
|
||||||
|
utils::npm(["run", "setup"]).dir(frontend_dir()).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_wasm(release: bool, native: bool) -> Result<(), Error> {
|
||||||
|
sequence(build_wasm_steps(release, native)).wait();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_wasm_steps(release: bool, native: bool) -> Vec<Expression> {
|
||||||
|
let wasm_artifact = target_dir().join(WASM_TARGET).join(if release { "release" } else { "debug" }).join(format!("{OUT_NAME}.wasm"));
|
||||||
|
let pkg_dir = frontend_dir().join("wrapper").join("pkg");
|
||||||
|
|
||||||
|
let mut steps = vec![
|
||||||
|
cmd!("cargo", "build", "--lib", "--package", WRAPPER_CRATE, "--target", WASM_TARGET)
|
||||||
|
.arg_if(release, "--release")
|
||||||
|
.args_if(native, ["--no-default-features", "--features", "native"])
|
||||||
|
.dir(workspace_dir()),
|
||||||
|
cmd!("wasm-bindgen", "--target", "web", "--out-name", OUT_NAME, "--out-dir", &pkg_dir, &wasm_artifact)
|
||||||
|
.arg_if(release, "--no-demangle")
|
||||||
|
.arg_if(!release, "--debug"),
|
||||||
|
];
|
||||||
|
|
||||||
|
if release {
|
||||||
|
let wasm_file = pkg_dir.join(format!("{OUT_NAME}_bg.wasm"));
|
||||||
|
steps.push(cmd!("wasm-opt", "-Os", "-g", &wasm_file, "-o", &wasm_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
steps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vite() -> Expression {
|
||||||
|
utils::node_bin("vite/bin/vite.js").dir(frontend_dir()).env("CARGO_TARGET_DIR", target_dir())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn watch(release: bool) -> Result<(), Error> {
|
||||||
|
use crate::cmd::prelude::*;
|
||||||
|
|
||||||
|
setup()?;
|
||||||
|
build_wasm(release, false)?;
|
||||||
|
|
||||||
|
let vite = vite().env("FORCE_COLOR", "1").env("CARGO_TERM_COLOR", "always");
|
||||||
|
let rust = utils::internal("watch")
|
||||||
|
.arg_if(release, "release")
|
||||||
|
.dir(workspace_dir())
|
||||||
|
.env("CARGO_TARGET_DIR", target_dir())
|
||||||
|
.env("CARGO_TERM_COLOR", "always");
|
||||||
|
|
||||||
|
supervise([("VITE", TerminalColor::Magenta, vite), ("RUST", TerminalColor::Blue, rust)])
|
||||||
|
}
|
||||||
+14
-58
@@ -1,6 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process;
|
|
||||||
|
|
||||||
|
pub mod cmd;
|
||||||
|
pub mod frontend;
|
||||||
pub mod requirements;
|
pub mod requirements;
|
||||||
|
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
@@ -65,70 +66,25 @@ impl Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(command: &str) -> Result<(), Error> {
|
pub fn workspace_dir() -> PathBuf {
|
||||||
run_from(command, None)
|
PathBuf::from(env!("CARGO_WORKSPACE_DIR"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn npm_run_in_frontend_dir(args: &str) -> Result<(), Error> {
|
pub fn target_dir() -> PathBuf {
|
||||||
let workspace_dir = std::path::PathBuf::from(env!("CARGO_WORKSPACE_DIR"));
|
match std::env::var_os("CARGO_TARGET_DIR") {
|
||||||
let frontend_dir = workspace_dir.join("frontend");
|
Some(custom_dir) => workspace_dir().join(custom_dir),
|
||||||
let npm = if cfg!(target_os = "windows") { "npm.cmd" } else { "npm" };
|
None => workspace_dir().join("target"),
|
||||||
run_from(&format!("{npm} run {args}"), Some(&frontend_dir))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open_url(url: &str) -> Result<(), Error> {
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let mut cmd = process::Command::new("cmd");
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
cmd.args(["/c", "start", url]);
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
let mut cmd = process::Command::new("open");
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
cmd.arg(url);
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
|
||||||
let mut cmd = process::Command::new("xdg-open");
|
|
||||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
|
||||||
cmd.arg(url);
|
|
||||||
|
|
||||||
let command_str = format!("{:?}", cmd);
|
|
||||||
let exit_code = cmd
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| Error::Io(e, format!("Failed to spawn command '{command_str}'")))?
|
|
||||||
.wait()
|
|
||||||
.map_err(|e| Error::Io(e, format!("Failed to wait for command '{command_str}'")))?;
|
|
||||||
if !exit_code.success() {
|
|
||||||
return Err(Error::Command(command_str, exit_code));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_from(command: &str, dir: Option<&PathBuf>) -> Result<(), Error> {
|
|
||||||
let command = command.split_whitespace().collect::<Vec<_>>();
|
|
||||||
let mut cmd = process::Command::new(command[0]);
|
|
||||||
if command.len() > 1 {
|
|
||||||
cmd.args(&command[1..]);
|
|
||||||
}
|
|
||||||
if let Some(dir) = dir {
|
|
||||||
cmd.current_dir(dir);
|
|
||||||
}
|
|
||||||
let exit_code = cmd
|
|
||||||
.spawn()
|
|
||||||
.map_err(|e| Error::Io(e, format!("Failed to spawn command '{}'", command.join(" "))))?
|
|
||||||
.wait()
|
|
||||||
.map_err(|e| Error::Io(e, format!("Failed to wait for command '{}'", command.join(" "))))?;
|
|
||||||
if !exit_code.success() {
|
|
||||||
return Err(Error::Command(command.join(" "), exit_code));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("One or more requirements were not met")]
|
||||||
|
RequirementsNotMet,
|
||||||
|
|
||||||
#[error("{1}: {0}")]
|
#[error("{1}: {0}")]
|
||||||
Io(#[source] std::io::Error, String),
|
Io(#[source] std::io::Error, String),
|
||||||
|
/// Used by the duct-based `cmd` module; folds in `Command` once call sites are migrated.
|
||||||
#[error("Command '{0}' exited with code {1}")]
|
#[error("{0}")]
|
||||||
Command(String, process::ExitStatus),
|
Command(#[source] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-23
@@ -1,5 +1,6 @@
|
|||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
|
|
||||||
|
use cargo_run::cmd::prelude::*;
|
||||||
use cargo_run::*;
|
use cargo_run::*;
|
||||||
|
|
||||||
fn usage() {
|
fn usage() {
|
||||||
@@ -33,6 +34,8 @@ fn usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
prepend_path();
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
let args: Vec<&str> = args.iter().skip(1).map(String::as_str).collect();
|
let args: Vec<&str> = args.iter().skip(1).map(String::as_str).collect();
|
||||||
|
|
||||||
@@ -67,9 +70,9 @@ fn explore_usage() {
|
|||||||
fn run_task(task: &Task) -> Result<(), Error> {
|
fn run_task(task: &Task) -> Result<(), Error> {
|
||||||
if let Action::Explore(tool) = &task.action {
|
if let Action::Explore(tool) = &task.action {
|
||||||
match tool.as_deref() {
|
match tool.as_deref() {
|
||||||
Some("bisect") => return open_url("https://graphite.art/volunteer/guide/codebase-overview/debugging-tips/#build-bisect-tool"),
|
Some("bisect") => return utils::open_url("https://graphite.art/volunteer/guide/codebase-overview/debugging-tips/#build-bisect-tool"),
|
||||||
Some("deps") => return open_url("https://graphite.art/volunteer/guide/codebase-overview/#crate-dependency-graph"),
|
Some("deps") => return utils::open_url("https://graphite.art/volunteer/guide/codebase-overview/#crate-dependency-graph"),
|
||||||
Some("editor") => return open_url("https://graphite.art/volunteer/guide/codebase-overview/editor-structure/#editor-outline"),
|
Some("editor") => return utils::open_url("https://graphite.art/volunteer/guide/codebase-overview/editor-structure/#editor-outline"),
|
||||||
None | Some("--help") => {
|
None | Some("--help") => {
|
||||||
explore_usage();
|
explore_usage();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -85,11 +88,19 @@ fn run_task(task: &Task) -> Result<(), Error> {
|
|||||||
requirements::check(task)?;
|
requirements::check(task)?;
|
||||||
|
|
||||||
match (&task.action, &task.target, &task.profile) {
|
match (&task.action, &task.target, &task.profile) {
|
||||||
(Action::Run, Target::Web, Profile::Debug | Profile::Default) => npm_run_in_frontend_dir("start")?,
|
(Action::Run, Target::Web, Profile::Debug | Profile::Default) => frontend::watch(false)?,
|
||||||
(Action::Run, Target::Web, Profile::Release) => npm_run_in_frontend_dir("production")?,
|
(Action::Run, Target::Web, Profile::Release) => frontend::watch(true)?,
|
||||||
|
|
||||||
(Action::Build, Target::Web, Profile::Debug) => npm_run_in_frontend_dir("build-dev")?,
|
(Action::Build, Target::Web, Profile::Debug) => {
|
||||||
(Action::Build, Target::Web, Profile::Release | Profile::Default) => npm_run_in_frontend_dir("build")?,
|
frontend::setup()?;
|
||||||
|
frontend::build_wasm(false, false)?;
|
||||||
|
frontend::vite().args(["build", "--mode", "dev"]).run()?;
|
||||||
|
}
|
||||||
|
(Action::Build, Target::Web, Profile::Release | Profile::Default) => {
|
||||||
|
frontend::setup()?;
|
||||||
|
frontend::build_wasm(true, false)?;
|
||||||
|
frontend::vite().args(["build"]).run()?;
|
||||||
|
}
|
||||||
|
|
||||||
(action, Target::Desktop, mut profile) => {
|
(action, Target::Desktop, mut profile) => {
|
||||||
if matches!(profile, Profile::Default) {
|
if matches!(profile, Profile::Default) {
|
||||||
@@ -100,34 +111,41 @@ fn run_task(task: &Task) -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(profile, Profile::Release) {
|
// Build the editor's Wasm module with the `native` feature, then bundle the frontend with Vite
|
||||||
npm_run_in_frontend_dir("build-native")?;
|
frontend::setup()?;
|
||||||
} else {
|
frontend::build_wasm(matches!(profile, Profile::Release), true)?;
|
||||||
npm_run_in_frontend_dir("build-native-dev")?;
|
frontend::vite().args(["build", "--mode", "native"]).run()?;
|
||||||
};
|
|
||||||
|
|
||||||
run("cargo run -p third-party-licenses --features desktop")?;
|
cmd!("cargo", "run", "-p", "third-party-licenses", "--features", "desktop").run()?;
|
||||||
|
|
||||||
let cargo_profile = match profile {
|
let cargo_profile = match profile {
|
||||||
Profile::Debug => "dev",
|
Profile::Debug => "dev",
|
||||||
Profile::Release => "release",
|
Profile::Release => "release",
|
||||||
Profile::Default => unreachable!(),
|
Profile::Default => unreachable!(),
|
||||||
};
|
};
|
||||||
let args = if matches!(action, Action::Run) {
|
cmd!("cargo", "run", "--profile", cargo_profile, "-p", "graphite-desktop-bundle")
|
||||||
format!(" -- open {}", task.args.join(" "))
|
.args_if(matches!(action, Action::Run), ["--", "open"].into_iter().chain(task.args.iter().map(String::as_str)))
|
||||||
} else {
|
.run()?;
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
run(&format!("cargo run --profile {cargo_profile} -p graphite-desktop-bundle{args}"))?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(Action::Run, Target::Cli, Profile::Debug | Profile::Default) => run(&format!("cargo run -p graphene-cli -- {}", task.args.join(" ")))?,
|
(Action::Run, Target::Cli, Profile::Debug | Profile::Default) => cmd!("cargo", "run", "-p", "graphene-cli", "--").args(&task.args).run()?,
|
||||||
(Action::Run, Target::Cli, Profile::Release) => run(&format!("cargo run -r -p graphene-cli -- {}", task.args.join(" ")))?,
|
(Action::Run, Target::Cli, Profile::Release) => cmd!("cargo", "run", "-r", "-p", "graphene-cli", "--").args(&task.args).run()?,
|
||||||
|
|
||||||
(Action::Build, Target::Cli, Profile::Debug) => run("cargo build -p graphene-cli")?,
|
(Action::Build, Target::Cli, Profile::Debug) => cmd!("cargo", "build", "-p", "graphene-cli").run()?,
|
||||||
(Action::Build, Target::Cli, Profile::Release | Profile::Default) => run("cargo build -r -p graphene-cli")?,
|
(Action::Build, Target::Cli, Profile::Release | Profile::Default) => cmd!("cargo", "build", "-r", "-p", "graphene-cli").run()?,
|
||||||
|
|
||||||
(Action::Explore(_), _, _) => unreachable!(),
|
(Action::Explore(_), _, _) => unreachable!(),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepend_path() {
|
||||||
|
let mut paths = vec![target_dir().join("cargo-run").join("bin")];
|
||||||
|
if let Some(path) = std::env::var_os("PATH") {
|
||||||
|
paths.extend(std::env::split_paths(&path));
|
||||||
|
}
|
||||||
|
if let Ok(joined) = std::env::join_paths(paths) {
|
||||||
|
// Safety: this runs before any other threads are spawned
|
||||||
|
unsafe { std::env::set_var("PATH", joined) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
|
use semver::{Version, VersionReq};
|
||||||
use std::io::IsTerminal;
|
use std::io::IsTerminal;
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
|
use crate::cmd::prelude::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Default, Clone)]
|
||||||
struct Requirement {
|
pub struct Requirement {
|
||||||
command: &'static str,
|
command: &'static str,
|
||||||
args: &'static [&'static str],
|
args: &'static [&'static str],
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
check: Check,
|
||||||
version: Option<&'static str>,
|
version: Option<&'static str>,
|
||||||
install: Option<&'static str>,
|
install: InstallAction,
|
||||||
skip: Option<&'static dyn Fn(&Task) -> bool>,
|
skip: Option<&'static dyn Fn(&Task) -> bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,43 +24,43 @@ fn requirements(task: &Task) -> Vec<Requirement> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Requirement {
|
Requirement {
|
||||||
command: "cargo-about",
|
command: "rustc",
|
||||||
args: &["--version"],
|
args: &["--print", "target-libdir", "--target", "wasm32-unknown-unknown"],
|
||||||
name: "cargo-about",
|
check: Check::Matches(&|out| std::path::Path::new(out.trim()).is_dir()),
|
||||||
install: Some("cargo install cargo-about"),
|
name: "Rust Wasm Toolchain",
|
||||||
|
install: "rustup target add wasm32-unknown-unknown".into(),
|
||||||
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Requirement {
|
Requirement {
|
||||||
command: "cargo-watch",
|
command: "cargo-about",
|
||||||
args: &["--version"],
|
args: &["--version"],
|
||||||
name: "cargo-watch",
|
name: "Cargo About",
|
||||||
install: Some("cargo install cargo-watch"),
|
install: "cargo install cargo-about".into(),
|
||||||
|
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Requirement {
|
||||||
|
command: "wasm-opt",
|
||||||
|
args: &["--version"],
|
||||||
|
name: "Wasm Opt",
|
||||||
|
version: Some(">=130"),
|
||||||
skip: Some(&|task| {
|
skip: Some(&|task| {
|
||||||
!matches!(
|
matches!(task.target, Target::Cli)
|
||||||
task,
|
|| match task.profile {
|
||||||
Task {
|
Profile::Debug => true,
|
||||||
target: Target::Web,
|
Profile::Release => false,
|
||||||
action: Action::Run,
|
Profile::Default => matches!(task.action, Action::Run),
|
||||||
..
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Requirement {
|
Requirement {
|
||||||
command: "wasm-bindgen",
|
command: "wasm-bindgen",
|
||||||
args: &["--version"],
|
args: &["--version"],
|
||||||
name: "wasm-bindgen-cli",
|
name: "Wasm Bindgen",
|
||||||
version: Some("0.2.121"),
|
version: Some("=0.2.121"),
|
||||||
install: Some("cargo install -f wasm-bindgen-cli@0.2.121"),
|
install: "cargo install -f wasm-bindgen-cli@0.2.121".into(),
|
||||||
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
|
||||||
},
|
|
||||||
Requirement {
|
|
||||||
command: "wasm-pack",
|
|
||||||
args: &["--version"],
|
|
||||||
name: "wasm-pack",
|
|
||||||
install: Some("cargo install wasm-pack"),
|
|
||||||
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
skip: Some(&|task| matches!(task.target, Target::Cli)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -98,24 +100,44 @@ pub fn check(task: &Task) -> Result<(), Error> {
|
|||||||
let mut failures: Vec<String> = Vec::new();
|
let mut failures: Vec<String> = Vec::new();
|
||||||
|
|
||||||
for dep in requirements(task) {
|
for dep in requirements(task) {
|
||||||
match Command::new(dep.command).args(dep.args).output() {
|
match cmd(dep.command, dep.args.iter().copied()).output_unchecked() {
|
||||||
Ok(output) if output.status.success() => {
|
Ok(output) if output.status.success() => {
|
||||||
let version = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
let version = version.lines().next().unwrap_or_default().trim();
|
match dep.check {
|
||||||
|
Check::PrintsVersion => {
|
||||||
if let Some(expected) = dep.version {
|
let line = stdout.lines().next().unwrap_or_default().trim();
|
||||||
if version.contains(expected) {
|
match dep.version {
|
||||||
eprintln!(" ✓ {} ({})", dep.name, version);
|
None => eprintln!(" ✓ {} ({line})", dep.name),
|
||||||
} else {
|
Some(req_str) => {
|
||||||
eprintln!(" ✗ {} (found {}, expected {})", dep.name, version, expected);
|
let req = VersionReq::parse(req_str).expect("invalid semver requirement");
|
||||||
if dep.install.is_some() {
|
match extract_version(line) {
|
||||||
installable.push(dep);
|
Some(version) if req.matches(&version) => eprintln!(" ✓ {} ({version})", dep.name),
|
||||||
|
Some(version) => {
|
||||||
|
eprintln!(" ✗ {} (found {version}, requires {req_str})", dep.name);
|
||||||
|
if dep.install.is_some() {
|
||||||
|
installable.push(dep);
|
||||||
|
} else {
|
||||||
|
failures.push(format!("{}: version mismatch (found {version}, requires {req_str})", dep.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!(" ✗ {} (could not parse version from '{line}')", dep.name);
|
||||||
|
failures.push(format!("{}: could not parse version from '{line}'", dep.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Check::Matches(check) => {
|
||||||
|
if !check(stdout.to_string()) {
|
||||||
|
eprintln!(" ✗ {} - check failed", dep.name);
|
||||||
|
if dep.install.is_some() {
|
||||||
|
installable.push(dep);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
failures.push(format!("{}: version mismatch (found {version}, expected {expected})", dep.name));
|
eprintln!(" ✓ {}", dep.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
eprintln!(" ✓ {} ({})", dep.name, version);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
@@ -147,7 +169,7 @@ pub fn check(task: &Task) -> Result<(), Error> {
|
|||||||
let total = installable.len() + failures.len();
|
let total = installable.len() + failures.len();
|
||||||
eprintln!("{total} requirement{} not met:", if total > 1 { "s" } else { "" });
|
eprintln!("{total} requirement{} not met:", if total > 1 { "s" } else { "" });
|
||||||
for dep in &installable {
|
for dep in &installable {
|
||||||
eprintln!(" - {}: {}", dep.name, dep.install.unwrap());
|
eprintln!(" - {}", dep.name);
|
||||||
}
|
}
|
||||||
for msg in &failures {
|
for msg in &failures {
|
||||||
eprintln!(" - {msg}");
|
eprintln!(" - {msg}");
|
||||||
@@ -158,43 +180,149 @@ pub fn check(task: &Task) -> Result<(), Error> {
|
|||||||
eprintln!("See: https://graphite.art/volunteer/guide/project-setup/");
|
eprintln!("See: https://graphite.art/volunteer/guide/project-setup/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let is_interactive = std::io::stdout().is_terminal() && std::io::stderr().is_terminal() && std::io::stdin().is_terminal();
|
||||||
|
|
||||||
// Don't prompt for automatic installation if we're not interactive session
|
// Don't prompt for automatic installation if we're not interactive session
|
||||||
if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() || !std::io::stdin().is_terminal() {
|
if !is_interactive {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if installable.is_empty() {
|
if installable.is_empty() && failures.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!();
|
if !installable.is_empty() {
|
||||||
eprintln!("The following can be installed automatically:");
|
eprintln!();
|
||||||
for dep in &installable {
|
eprintln!("The following can be installed automatically:");
|
||||||
eprintln!(" {}", dep.install.unwrap());
|
|
||||||
}
|
|
||||||
eprintln!();
|
|
||||||
if installable.len() == 1 {
|
|
||||||
eprint!("Install it now? [Y/n] ");
|
|
||||||
} else {
|
|
||||||
eprint!("Install them now? [Y/n] ");
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut input = String::new();
|
|
||||||
std::io::stdin().read_line(&mut input).map_err(|e| Error::Io(e, "Failed to read from stdin".into()))?;
|
|
||||||
let input = input.trim();
|
|
||||||
|
|
||||||
if input.is_empty() || input.eq_ignore_ascii_case("y") || input.eq_ignore_ascii_case("yes") {
|
|
||||||
for dep in &installable {
|
for dep in &installable {
|
||||||
let parts: Vec<&str> = dep.install.unwrap().split_whitespace().collect();
|
eprintln!(" {}: {}", dep.name, dep.install.description());
|
||||||
eprintln!("Running: {}...", dep.install.unwrap());
|
}
|
||||||
let status = Command::new(parts[0])
|
eprintln!();
|
||||||
.args(&parts[1..])
|
if installable.len() == 1 {
|
||||||
.status()
|
eprint!("Install it now? [Y/n] ");
|
||||||
.map_err(|e| Error::Io(e, format!("Failed to run '{}'", dep.install.unwrap())))?;
|
} else {
|
||||||
if !status.success() {
|
eprint!("Install them now? [Y/n] ");
|
||||||
eprintln!("Failed to install {}", dep.name);
|
}
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).map_err(|e| Error::Io(e, "Failed to read from stdin".into()))?;
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.eq_ignore_ascii_case("y") || input.eq_ignore_ascii_case("yes") {
|
||||||
|
let mut successfully_installed = Vec::new();
|
||||||
|
|
||||||
|
for (i, dep) in installable.iter().enumerate() {
|
||||||
|
eprintln!("Installing {}...", dep.name);
|
||||||
|
match &dep.install {
|
||||||
|
InstallAction::Command(install_cmd) => {
|
||||||
|
let parts: Vec<&str> = install_cmd.split_whitespace().collect();
|
||||||
|
let expr = cmd(parts[0], parts[1..].iter().copied()).unchecked();
|
||||||
|
match Expression::run(&expr) {
|
||||||
|
Ok(output) if output.status.success() => successfully_installed.push(i),
|
||||||
|
Ok(_) => eprintln!("Failed to install {}", dep.name),
|
||||||
|
Err(e) => return Err(Error::Command(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstallAction::Function { function, .. } => {
|
||||||
|
if let Err(e) = function() {
|
||||||
|
eprintln!("{e}");
|
||||||
|
eprintln!("Failed to install {}", dep.name);
|
||||||
|
} else {
|
||||||
|
successfully_installed.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InstallAction::None => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i in successfully_installed.into_iter().rev() {
|
||||||
|
installable.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !failures.is_empty() {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("The following requirements must be resolved manually:");
|
||||||
|
for msg in &failures {
|
||||||
|
eprintln!(" - {msg}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!installable.is_empty() || !failures.is_empty()) && is_interactive {
|
||||||
|
eprintln!();
|
||||||
|
eprintln!("Continue without resolving these requirements? [y/N]");
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).map_err(|e| Error::Io(e, "Failed to read from stdin".into()))?;
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.eq_ignore_ascii_case("n") || input.eq_ignore_ascii_case("no") {
|
||||||
|
return Err(Error::RequirementsNotMet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_version(line: &str) -> Option<Version> {
|
||||||
|
line.split_whitespace().find_map(|token| {
|
||||||
|
let token = token.trim_start_matches('v').trim_end_matches(|c: char| !c.is_ascii_alphanumeric());
|
||||||
|
if token.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Ok(version) = Version::parse(token) {
|
||||||
|
return Some(version);
|
||||||
|
}
|
||||||
|
let (core, suffix) = match token.find(['-', '+']) {
|
||||||
|
Some(i) => token.split_at(i),
|
||||||
|
None => (token, ""),
|
||||||
|
};
|
||||||
|
let parts: Vec<&str> = core.split('.').collect();
|
||||||
|
if parts.iter().any(|p| p.is_empty() || !p.chars().all(|c| c.is_ascii_digit())) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let major = parts[0];
|
||||||
|
let minor = parts.get(1).copied().unwrap_or("0");
|
||||||
|
let patch = parts.get(2).copied().unwrap_or("0");
|
||||||
|
Version::parse(&format!("{major}.{minor}.{patch}{suffix}")).ok()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
enum Check {
|
||||||
|
#[default]
|
||||||
|
PrintsVersion,
|
||||||
|
Matches(&'static dyn Fn(String) -> bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
enum InstallAction {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Command(&'static str),
|
||||||
|
#[expect(dead_code)] // TODO: Remove after followup pr landed
|
||||||
|
Function {
|
||||||
|
description: &'static str,
|
||||||
|
function: &'static dyn Fn() -> Result<(), Error>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstallAction {
|
||||||
|
fn is_some(&self) -> bool {
|
||||||
|
!matches!(self, InstallAction::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
InstallAction::None => "",
|
||||||
|
InstallAction::Command(cmd) => cmd,
|
||||||
|
InstallAction::Function { description, .. } => description,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for InstallAction {
|
||||||
|
fn from(value: &'static str) -> Self {
|
||||||
|
InstallAction::Command(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ fn parse(parsed: Output) -> Vec<LicenseEntry> {
|
|||||||
|
|
||||||
fn run() -> Result<Output, Error> {
|
fn run() -> Result<Output, Error> {
|
||||||
let output = Command::new("cargo")
|
let output = Command::new("cargo")
|
||||||
.args(["about", "generate", "--format", "json", "--frozen"])
|
.args(["about", "generate", "--format", "json", "--locked"])
|
||||||
.current_dir(env!("CARGO_WORKSPACE_DIR"))
|
.current_dir(env!("CARGO_WORKSPACE_DIR"))
|
||||||
.output()
|
.output()
|
||||||
.map_err(|e| Error::Io(e, "Failed to run cargo about generate".into()))?;
|
.map_err(|e| Error::Io(e, "Failed to run cargo about generate".into()))?;
|
||||||
|
|||||||
@@ -98,10 +98,9 @@ fn run() -> Result<(), Error> {
|
|||||||
let current_hash = format!("{:016x}", hasher.finish());
|
let current_hash = format!("{:016x}", hasher.finish());
|
||||||
|
|
||||||
if current_hash == fs::read_to_string(¤t_hash_path).unwrap_or_default() {
|
if current_hash == fs::read_to_string(¤t_hash_path).unwrap_or_default() {
|
||||||
eprintln!("No changes in licenses detected, skipping generation.");
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
eprintln!("Changes in licenses detected, generating new license file.");
|
eprintln!("Changes in licenses detected, generating new license file...");
|
||||||
|
|
||||||
let licenses = merge_filter_dedup_and_sort(vec![
|
let licenses = merge_filter_dedup_and_sort(vec![
|
||||||
cargo_source.licenses()?,
|
cargo_source.licenses()?,
|
||||||
|
|||||||
Reference in New Issue
Block a user