mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
[red-knot] Add basic WASM API (#12654)
This commit is contained in:
parent
f0318ff889
commit
10e977d5f5
11 changed files with 473 additions and 31 deletions
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
|
@ -111,7 +111,7 @@ jobs:
|
|||
- name: "Clippy"
|
||||
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
|
||||
- name: "Clippy (wasm)"
|
||||
run: cargo clippy -p ruff_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
run: cargo clippy -p ruff_wasm -p red_knot_wasm --target wasm32-unknown-unknown --all-features --locked -- -D warnings
|
||||
|
||||
cargo-test-linux:
|
||||
name: "cargo test (linux)"
|
||||
|
@ -191,10 +191,14 @@ jobs:
|
|||
cache-dependency-path: playground/package-lock.json
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "Run wasm-pack"
|
||||
- name: "Test ruff_wasm"
|
||||
run: |
|
||||
cd crates/ruff_wasm
|
||||
wasm-pack test --node
|
||||
- name: "Test red_knot_wasm"
|
||||
run: |
|
||||
cd crates/red_knot_wasm
|
||||
wasm-pack test --node
|
||||
|
||||
cargo-build-release:
|
||||
name: "cargo build (release)"
|
||||
|
|
90
Cargo.lock
generated
90
Cargo.lock
generated
|
@ -20,7 +20,7 @@ version = "0.8.11"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
|
@ -270,6 +270,12 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -459,7 +465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
|
@ -486,7 +492,7 @@ version = "0.1.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
|
@ -523,7 +529,7 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -673,7 +679,7 @@ version = "5.5.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
|
@ -686,7 +692,7 @@ version = "6.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
|
@ -810,7 +816,7 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"home",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
@ -836,7 +842,7 @@ version = "0.2.23"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"windows-sys 0.52.0",
|
||||
|
@ -900,7 +906,7 @@ version = "0.2.14"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
|
@ -932,7 +938,7 @@ version = "2.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
|
@ -1141,7 +1147,7 @@ version = "0.1.12"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1390,6 +1396,12 @@ version = "2.7.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.43"
|
||||
|
@ -1448,7 +1460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
@ -1574,7 +1586,7 @@ version = "0.9.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
|
@ -1928,6 +1940,22 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "red_knot_wasm"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"js-sys",
|
||||
"log",
|
||||
"red_knot_workspace",
|
||||
"ruff_db",
|
||||
"ruff_notebook",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-test",
|
||||
"wee_alloc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "red_knot_workspace"
|
||||
version = "0.0.0"
|
||||
|
@ -2016,7 +2044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"spin",
|
||||
|
@ -2134,6 +2162,7 @@ dependencies = [
|
|||
"salsa",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
"web-time",
|
||||
"zip",
|
||||
]
|
||||
|
||||
|
@ -2989,7 +3018,7 @@ version = "3.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
|
@ -3034,7 +3063,7 @@ version = "3.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -3078,7 +3107,7 @@ version = "1.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
|
@ -3480,7 +3509,7 @@ version = "0.2.92"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
|
@ -3505,7 +3534,7 @@ version = "0.4.42"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
|
@ -3575,6 +3604,16 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.1"
|
||||
|
@ -3584,6 +3623,18 @@ dependencies = [
|
|||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wee_alloc"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.1"
|
||||
|
@ -3858,6 +3909,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ walkdir = { version = "2.3.2" }
|
|||
wasm-bindgen = { version = "0.2.92" }
|
||||
wasm-bindgen-test = { version = "0.3.42" }
|
||||
wild = { version = "2" }
|
||||
zip = { version = "0.6.6", default-features = false, features = ["zstd"] }
|
||||
zip = { version = "0.6.6", default-features = false }
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "warn"
|
||||
|
|
|
@ -25,7 +25,7 @@ zip = { workspace = true }
|
|||
[build-dependencies]
|
||||
path-slash = { workspace = true }
|
||||
walkdir = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
zip = { workspace = true, features = ["zstd", "deflate"] }
|
||||
|
||||
[dev-dependencies]
|
||||
ruff_db = { workspace = true, features = ["os"] }
|
||||
|
|
|
@ -23,8 +23,21 @@ const TYPESHED_ZIP_LOCATION: &str = "/zipped_typeshed.zip";
|
|||
fn zip_dir(directory_path: &str, writer: File) -> ZipResult<File> {
|
||||
let mut zip = ZipWriter::new(writer);
|
||||
|
||||
// Use deflated compression for WASM builds because compiling `zstd-sys` requires clang
|
||||
// [source](https://github.com/gyscos/zstd-rs/wiki/Compile-for-WASM) which complicates the build
|
||||
// by a lot. Deflated compression is slower but it shouldn't matter much for the WASM use case
|
||||
// (WASM itself is already slower than a native build for a specific platform).
|
||||
// We can't use `#[cfg(...)]` here because the target-arch in a build script is the
|
||||
// architecture of the system running the build script and not the architecture of the build-target.
|
||||
// That's why we use the `TARGET` environment variable here.
|
||||
let method = if std::env::var("TARGET").unwrap().contains("wasm32") {
|
||||
CompressionMethod::Deflated
|
||||
} else {
|
||||
CompressionMethod::Zstd
|
||||
};
|
||||
|
||||
let options = FileOptions::default()
|
||||
.compression_method(CompressionMethod::Zstd)
|
||||
.compression_method(method)
|
||||
.unix_permissions(0o644);
|
||||
|
||||
for entry in walkdir::WalkDir::new(directory_path) {
|
||||
|
|
38
crates/red_knot_wasm/Cargo.toml
Normal file
38
crates/red_knot_wasm/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
|||
[package]
|
||||
name = "red_knot_wasm"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
authors = { workspace = true }
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
homepage = { workspace = true }
|
||||
documentation = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
license = { workspace = true }
|
||||
description = "WebAssembly bindings for Red Knot"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
|
||||
[dependencies]
|
||||
red_knot_workspace = { workspace = true }
|
||||
|
||||
ruff_db = { workspace = true }
|
||||
ruff_notebook = { workspace = true }
|
||||
|
||||
console_error_panic_hook = { workspace = true, optional = true }
|
||||
console_log = { workspace = true }
|
||||
js-sys = { workspace = true }
|
||||
log = { workspace = true }
|
||||
wasm-bindgen = { workspace = true }
|
||||
wee_alloc = "0.4.5"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
284
crates/red_knot_wasm/src/lib.rs
Normal file
284
crates/red_knot_wasm/src/lib.rs
Normal file
|
@ -0,0 +1,284 @@
|
|||
use std::any::Any;
|
||||
|
||||
use js_sys::Error;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use red_knot_workspace::db::RootDatabase;
|
||||
use red_knot_workspace::workspace::WorkspaceMetadata;
|
||||
use ruff_db::files::{system_path_to_file, File};
|
||||
use ruff_db::program::{ProgramSettings, SearchPathSettings};
|
||||
use ruff_db::system::walk_directory::WalkDirectoryBuilder;
|
||||
use ruff_db::system::{
|
||||
DirectoryEntry, MemoryFileSystem, Metadata, System, SystemPath, SystemPathBuf,
|
||||
SystemVirtualPath,
|
||||
};
|
||||
use ruff_notebook::Notebook;
|
||||
|
||||
// Use `wee_alloc` as the global allocator.
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
use log::Level;
|
||||
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong.");
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Workspace {
|
||||
db: RootDatabase,
|
||||
system: WasmSystem,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Workspace {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(root: &str, settings: &Settings) -> Result<Workspace, Error> {
|
||||
let system = WasmSystem::new(SystemPath::new(root));
|
||||
let workspace =
|
||||
WorkspaceMetadata::from_path(SystemPath::new(root), &system).map_err(into_error)?;
|
||||
|
||||
let program_settings = ProgramSettings {
|
||||
target_version: settings.target_version.into(),
|
||||
search_paths: SearchPathSettings::default(),
|
||||
};
|
||||
|
||||
let db = RootDatabase::new(workspace, program_settings, system.clone());
|
||||
|
||||
Ok(Self { db, system })
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "openFile")]
|
||||
pub fn open_file(&mut self, path: &str, contents: &str) -> Result<FileHandle, Error> {
|
||||
self.system
|
||||
.fs
|
||||
.write_file(path, contents)
|
||||
.map_err(into_error)?;
|
||||
|
||||
let file = system_path_to_file(&self.db, path).expect("File to exist");
|
||||
file.sync(&mut self.db);
|
||||
|
||||
self.db.workspace().open_file(&mut self.db, file);
|
||||
|
||||
Ok(FileHandle {
|
||||
file,
|
||||
path: SystemPath::new(path).to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "updateFile")]
|
||||
pub fn update_file(&mut self, file_id: &FileHandle, contents: &str) -> Result<(), Error> {
|
||||
if !self.system.fs.exists(&file_id.path) {
|
||||
return Err(Error::new("File does not exist"));
|
||||
}
|
||||
|
||||
self.system
|
||||
.fs
|
||||
.write_file(&file_id.path, contents)
|
||||
.map_err(into_error)?;
|
||||
|
||||
file_id.file.sync(&mut self.db);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "closeFile")]
|
||||
pub fn close_file(&mut self, file_id: &FileHandle) -> Result<(), Error> {
|
||||
let file = file_id.file;
|
||||
|
||||
self.db.workspace().close_file(&mut self.db, file);
|
||||
self.system
|
||||
.fs
|
||||
.remove_file(&file_id.path)
|
||||
.map_err(into_error)?;
|
||||
|
||||
file.sync(&mut self.db);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks a single file.
|
||||
#[wasm_bindgen(js_name = "checkFile")]
|
||||
pub fn check_file(&self, file_id: &FileHandle) -> Result<Vec<String>, Error> {
|
||||
let result = self.db.check_file(file_id.file).map_err(into_error)?;
|
||||
|
||||
Ok(result.to_vec())
|
||||
}
|
||||
|
||||
/// Checks all open files
|
||||
pub fn check(&self) -> Result<Vec<String>, Error> {
|
||||
let result = self.db.check().map_err(into_error)?;
|
||||
|
||||
Ok(result.clone())
|
||||
}
|
||||
|
||||
/// Returns the parsed AST for `path`
|
||||
pub fn parsed(&self, file_id: &FileHandle) -> Result<String, Error> {
|
||||
let parsed = ruff_db::parsed::parsed_module(&self.db, file_id.file);
|
||||
|
||||
Ok(format!("{:#?}", parsed.syntax()))
|
||||
}
|
||||
|
||||
/// Returns the token stream for `path` serialized as a string.
|
||||
pub fn tokens(&self, file_id: &FileHandle) -> Result<String, Error> {
|
||||
let parsed = ruff_db::parsed::parsed_module(&self.db, file_id.file);
|
||||
|
||||
Ok(format!("{:#?}", parsed.tokens()))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "sourceText")]
|
||||
pub fn source_text(&self, file_id: &FileHandle) -> Result<String, Error> {
|
||||
let source_text = ruff_db::source::source_text(&self.db, file_id.file);
|
||||
|
||||
Ok(source_text.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_error<E: std::fmt::Display>(err: E) -> Error {
|
||||
Error::new(&err.to_string())
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[wasm_bindgen(inspectable)]
|
||||
pub struct FileHandle {
|
||||
path: SystemPathBuf,
|
||||
file: File,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl FileHandle {
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn js_to_string(&self) -> String {
|
||||
format!("file(id: {:?}, path: {})", self.file, self.path)
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Settings {
|
||||
pub target_version: TargetVersion,
|
||||
}
|
||||
#[wasm_bindgen]
|
||||
impl Settings {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(target_version: TargetVersion) -> Self {
|
||||
Self { target_version }
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum TargetVersion {
|
||||
Py37,
|
||||
#[default]
|
||||
Py38,
|
||||
Py39,
|
||||
Py310,
|
||||
Py311,
|
||||
Py312,
|
||||
Py313,
|
||||
}
|
||||
|
||||
impl From<TargetVersion> for ruff_db::program::TargetVersion {
|
||||
fn from(value: TargetVersion) -> Self {
|
||||
match value {
|
||||
TargetVersion::Py37 => Self::Py37,
|
||||
TargetVersion::Py38 => Self::Py38,
|
||||
TargetVersion::Py39 => Self::Py39,
|
||||
TargetVersion::Py310 => Self::Py310,
|
||||
TargetVersion::Py311 => Self::Py311,
|
||||
TargetVersion::Py312 => Self::Py312,
|
||||
TargetVersion::Py313 => Self::Py313,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct WasmSystem {
|
||||
fs: MemoryFileSystem,
|
||||
}
|
||||
|
||||
impl WasmSystem {
|
||||
fn new(root: &SystemPath) -> Self {
|
||||
Self {
|
||||
fs: MemoryFileSystem::with_current_directory(root),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl System for WasmSystem {
|
||||
fn path_metadata(&self, path: &SystemPath) -> ruff_db::system::Result<Metadata> {
|
||||
self.fs.metadata(path)
|
||||
}
|
||||
|
||||
fn canonicalize_path(&self, path: &SystemPath) -> ruff_db::system::Result<SystemPathBuf> {
|
||||
Ok(self.fs.canonicalize(path))
|
||||
}
|
||||
|
||||
fn read_to_string(&self, path: &SystemPath) -> ruff_db::system::Result<String> {
|
||||
self.fs.read_to_string(path)
|
||||
}
|
||||
|
||||
fn read_to_notebook(
|
||||
&self,
|
||||
path: &SystemPath,
|
||||
) -> Result<ruff_notebook::Notebook, ruff_notebook::NotebookError> {
|
||||
let content = self.read_to_string(path)?;
|
||||
Notebook::from_source_code(&content)
|
||||
}
|
||||
|
||||
fn virtual_path_metadata(
|
||||
&self,
|
||||
_path: &SystemVirtualPath,
|
||||
) -> ruff_db::system::Result<Metadata> {
|
||||
Err(not_found())
|
||||
}
|
||||
|
||||
fn read_virtual_path_to_string(
|
||||
&self,
|
||||
_path: &SystemVirtualPath,
|
||||
) -> ruff_db::system::Result<String> {
|
||||
Err(not_found())
|
||||
}
|
||||
|
||||
fn read_virtual_path_to_notebook(
|
||||
&self,
|
||||
_path: &SystemVirtualPath,
|
||||
) -> Result<ruff_notebook::Notebook, ruff_notebook::NotebookError> {
|
||||
Err(ruff_notebook::NotebookError::Io(not_found()))
|
||||
}
|
||||
|
||||
fn current_directory(&self) -> &SystemPath {
|
||||
self.fs.current_directory()
|
||||
}
|
||||
|
||||
fn read_directory<'a>(
|
||||
&'a self,
|
||||
path: &SystemPath,
|
||||
) -> ruff_db::system::Result<
|
||||
Box<dyn Iterator<Item = ruff_db::system::Result<DirectoryEntry>> + 'a>,
|
||||
> {
|
||||
Ok(Box::new(self.fs.read_directory(path)?))
|
||||
}
|
||||
|
||||
fn walk_directory(&self, path: &SystemPath) -> WalkDirectoryBuilder {
|
||||
self.fs.walk_directory(path)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn not_found() -> std::io::Error {
|
||||
std::io::Error::new(std::io::ErrorKind::NotFound, "No such file or directory")
|
||||
}
|
21
crates/red_knot_wasm/tests/api.rs
Normal file
21
crates/red_knot_wasm/tests/api.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
use red_knot_wasm::{Settings, TargetVersion, Workspace};
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn check() {
|
||||
let settings = Settings {
|
||||
target_version: TargetVersion::Py312,
|
||||
};
|
||||
let mut workspace = Workspace::new("/", &settings).expect("Workspace to be created");
|
||||
|
||||
let test = workspace
|
||||
.open_file("test.py", "import random22\n")
|
||||
.expect("File to be opened");
|
||||
|
||||
let result = workspace.check_file(&test).expect("Check to succeed");
|
||||
|
||||
assert_eq!(result, vec!["Unresolved import 'random22'"]);
|
||||
}
|
|
@ -29,7 +29,13 @@ salsa = { workspace = true }
|
|||
path-slash = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
rustc-hash = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[target.'cfg(not(target_arch="wasm32"))'.dependencies]
|
||||
zip = { workspace = true, features = ["zstd"] }
|
||||
|
||||
[target.'cfg(target_arch="wasm32")'.dependencies]
|
||||
web-time = { version = "1.1.0" }
|
||||
zip = { workspace = true, features = ["deflate"] }
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { workspace = true }
|
||||
|
|
|
@ -77,7 +77,7 @@ impl std::fmt::Debug for TargetVersion {
|
|||
}
|
||||
|
||||
/// Configures the search paths for module resolution.
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
#[derive(Eq, PartialEq, Debug, Clone, Default)]
|
||||
pub struct SearchPathSettings {
|
||||
/// List of user-provided paths that should take first priority in the module resolution.
|
||||
/// Examples in other type checkers are mypy's MYPYPATH environment variable,
|
||||
|
|
|
@ -213,7 +213,7 @@ impl MemoryFileSystem {
|
|||
|
||||
let file = get_or_create_file(&mut by_path, &normalized)?;
|
||||
file.content = content.to_string();
|
||||
file.last_modified = FileTime::now();
|
||||
file.last_modified = now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ impl MemoryFileSystem {
|
|||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(File {
|
||||
content: content.to_string(),
|
||||
last_modified: FileTime::now(),
|
||||
last_modified: now(),
|
||||
});
|
||||
}
|
||||
std::collections::hash_map::Entry::Occupied(mut entry) => {
|
||||
|
@ -284,7 +284,7 @@ impl MemoryFileSystem {
|
|||
let mut by_path = self.inner.by_path.write().unwrap();
|
||||
let normalized = self.normalize_path(path.as_ref());
|
||||
|
||||
get_or_create_file(&mut by_path, &normalized)?.last_modified = FileTime::now();
|
||||
get_or_create_file(&mut by_path, &normalized)?.last_modified = now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ fn create_dir_all(
|
|||
path.push(component);
|
||||
let entry = paths.entry(path.clone()).or_insert_with(|| {
|
||||
Entry::Directory(Directory {
|
||||
last_modified: FileTime::now(),
|
||||
last_modified: now(),
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -472,7 +472,7 @@ fn get_or_create_file<'a>(
|
|||
let entry = paths.entry(normalized.to_path_buf()).or_insert_with(|| {
|
||||
Entry::File(File {
|
||||
content: String::new(),
|
||||
last_modified: FileTime::now(),
|
||||
last_modified: now(),
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -654,6 +654,30 @@ enum WalkerState {
|
|||
Nested { path: SystemPathBuf, depth: usize },
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn now() -> FileTime {
|
||||
FileTime::now()
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn now() -> FileTime {
|
||||
// Copied from FileTime::from_system_time()
|
||||
let time = web_time::SystemTime::now();
|
||||
|
||||
time.duration_since(web_time::UNIX_EPOCH)
|
||||
.map(|d| FileTime::from_unix_time(d.as_secs() as i64, d.subsec_nanos()))
|
||||
.unwrap_or_else(|e| {
|
||||
let until_epoch = e.duration();
|
||||
let (sec_offset, nanos) = if until_epoch.subsec_nanos() == 0 {
|
||||
(0, 0)
|
||||
} else {
|
||||
(-1, 1_000_000_000 - until_epoch.subsec_nanos())
|
||||
};
|
||||
|
||||
FileTime::from_unix_time(-(until_epoch.as_secs() as i64) + sec_offset, nanos)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::ErrorKind;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue