Skip to content

Commit

Permalink
Remove references to deleted crates (#728)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffcharles committed Aug 14, 2024
1 parent 27e2f2d commit 0a5972a
Show file tree
Hide file tree
Showing 8 changed files with 8 additions and 299 deletions.
2 changes: 0 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
crates/quickjs-wasm-sys/quickjs/* linguist-vendored

# Some people try to run shell scripts in WSL after cloning the repo in Windows.
# If they clone the repo in Windows and the default `core.autocrlf` settings
# are used, the shell script line endings are converted to Windows line
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,3 @@ tests/target/*.wasm

.idea
.vscode

crates/quickjs-wasm-sys/wasi-sdk
3 changes: 0 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,3 @@ clean: clean-wasi-sdk clean-cargo

clean-cargo:
cargo clean

clean-wasi-sdk:
rm -r crates/quickjs-wasm-sys/wasi-sdk 2> /dev/null || true
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,6 @@ $ wasmtime run --preload javy_quickjs_provider_v2=provider.wasm my_code.wasm
hello world!
```

## Using quickjs-wasm-rs to build your own toolchain

The `quickjs-wasm-rs` crate that is part of this project can be used as part of a Rust crate targeting Wasm to customize how that Rust crate interacts with QuickJS. This may be useful when trying to use JavaScript inside a Wasm module and Javy does not fit your needs as `quickjs-wasm-rs` contains serializers that make it easier to send structured data (for example, strings or objects) between host code and Wasm code.

## Releasing

1. Update the root `Cargo.toml` with the new version
Expand Down
221 changes: 3 additions & 218 deletions docs/contributing-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ flowchart TD
javy-cli --> wasm
subgraph wasm[javy_core.wasm / javy_quickjs_provider.wasm]
javy-core --> javy
javy-core --> javy-apis
javy-apis --> javy
javy --> quickjs-wasm-rs
quickjs-wasm-rs --> quickjs-wasm-sys
javy --> rquickjs
end
```

We anticipate most changes will be to the `javy`, `javy-apis`, and `quickjs-wasm-rs` crates.
We anticipate most changes will be to the `javy-cli` and `javy` crates.

### `javy`

Expand Down Expand Up @@ -83,203 +80,9 @@ Read the `config` and call the appropriate methods on `context` to apply the con
You should consider gating your feature by a Cargo feature when:

- Your feature would materially increase the size of the produced Wasm module.
- Your feature requires enabling additional features in the `quickjs-wasm-rs` crate.

These are guidelines and we're willing to discuss if a feature needs to be gated by a Cargo feature on a case-by-case basis.

### `javy-apis`

Common JS APIs for use with a `javy::Runtime`. For example, `console`, `TextEncoder`, `TextDecoder`. If there is a standard JS API that seems like it would be useful to multiple users of Javy, it should be implemented in this crate. If this is an API specific to your use case, you should define it in a crate of your own and register the implementation using a similar approach to how the APIs in this crate define their implementations.

#### Adding an API implementation

1. Add a feature to the crate's `Cargo.toml` for your module.
2. Create a directory under `src` with a `mod.rs` file.
3. If your API implementation requires configuration, create a configuration struct for the configuration properties required in your module.
4. If necessary, add any JS source files inside the module you're adding. If you can implement your API without JS, you don't need to add any.
5. In `mod.rs`, implement the `crate::JSApiSet` trait. If your API requires configuration, add the configuration struct defined earlier to the `crate::ApiConfig` struct under a `#[cfg(feature = "your feature name")]` attribute.
6. Add the `mod` to the crate's `lib.rs` under a `#[cfg(feature = "your feature name")]` attribute.
7. Add a call to your struct's `register` method under a `#[cfg(feature = "your feature name")]` in `lib.rs`'s `add_to_runtime` function.

##### Example

Here's a contrived example of adding an API to print an environment variable with a prefix that's configured when creating a Javy runtime. Normally this wouldn't go in this crate, but instead in your own crate since it's not a generally useful API.

Create the directory, `crates/apis/src/env_var_printer`.

In `crates/apis/Cargo.toml`:

```diff
[features]
console = []
+ env_var_printer = []
random = ["dep:fastrand"]
stream_io = []
text_encoding = []
```

In `crates/apis/src/env_var_printer/config.rs`:

```rust
use crate::APIConfig;

// Use crate visibility to avoid exposing the property outside the crate
#[derive(Debug)]
pub(crate) struct EnvVarConfig {
pub(super) prefix: String,
}

// Always have a default value for every config.
impl Default for EnvVarConfig {
fn default() -> Self {
Self {
prefix: "Default prefix: ".to_string(),
}
}
}

// Define one or more methods on `APIConfig`, not `EnvVarConfig`, to set properties.
impl APIConfig {
/// Sets the prefix for `Javy.Env.print`.
pub fn prefix(&mut self, prefix: String) -> &mut Self {
self.env_var.prefix = prefix;
self
}
}
```

In `crates/apis/src/env_var_printer/env-var-printer.js`:

```js
// Wrap everything in an anonymous function to avoid leaking local variables into the global scope.
(function () {
// Get a reference to the function before we delete it from `globalThis`.
const __javy_env_printEnvVar = globalThis.__javy_env_printVal;
globalThis.Javy.Env = {
print(name) {
__javy_env_printEnvVar(name);
},
};
// Delete the function from `globalThis` so it doesn't leak.
Reflect.deleteProperty(globalThis, "__javy_env_printVal");
})();
```

For something this simple, you don't need a JS file, I'm including it to demonstrate how things would be wired up.

In `crates/apis/src/env_var_printer/mod.rs`:

```rust
use std::env;

use anyhow::{bail, Result};
use javy::{quickjs::JSValue, Runtime};

use crate::{APIConfig, JSApiSet};
pub(super) use config::EnvVarConfig;

mod config;

pub(super) struct EnvVarPrinter;

impl JSApiSet for EnvVarPrinter {
fn register(&self, runtime: &Runtime, config: &APIConfig) -> Result<()> {
let context = runtime.context();

let global = context.global_object()?;

let mut javy_object = global.get_property("Javy")?;

// If you're defining something on the `Javy` object, ensure it exists.
if javy_object.is_undefined() {
javy_object = context.object_value()?;
global.set_property("Javy", javy_object)?;
}

// `wrap_callback`` has a static lifetime so you can't use references to the config in its body.
let prefix = config.env_var.prefix.clone();
global.set_property(
"__javy_env_printVal",
context.wrap_callback(move |_ctx, _this, args| {
let [name] = args else {
bail!("Incorrect number of arguments");
};
// Convert JSValueRefs to Rust types.
let name: String = name.try_into()?;
println!("{}{}", prefix, env::var(name)?);
Ok(JSValue::Undefined)
})?,
)?;

context.eval_global("env-var-printer.js", include_str!("env-var-printer.js"))?;

Ok(())
}
}

// Automated tests are highly recommended
#[cfg(test)]
mod tests {
use std::env;

use crate::{APIConfig, JSApiSet};
use anyhow::Result;
use javy::Runtime;

use super::EnvVarPrinter;

#[test]
fn test_print_env_var() -> Result<()> {
let runtime = Runtime::default();
let context = runtime.context();
EnvVarPrinter.register(&runtime, &APIConfig::default())?;
env::set_var("HELLO", "there");
let _ = context.eval_global("main", "Javy.Env.print('HELLO');")?;
env::remove_var("HELLO");
Ok(())
}
}
```

In `crates/apis/src/api_config.rs`:

```diff
#[derive(Debug, Default)]
pub struct APIConfig {
#[cfg(feature = "console")]
pub(crate) console: crate::console::ConsoleConfig,
+ #[cfg(feature = "env_var_printer")]
+ pub(crate) env_var: crate::env_var_printer::EnvVarConfig,
}
```

In `crates/apis/src/lib.rs`:

```diff
#[cfg(feature = "console")]
mod console;
+ #[cfg(feature = "env_var_printer")]
+ mod env_var_printer;
#[cfg(feature = "random")]
mod random;
```

and

```diff
pub fn add_to_runtime(runtime: &Runtime, config: APIConfig) -> Result<()> {
#[cfg(feature = "console")]
console::Console::new().register(runtime, &config)?;
+ #[cfg(feature = "env_var_printer")]
+ env_var_printer::EnvVarPrinter.register(runtime, &config)?;
#[cfg(feature = "random")]
random::Random.register(runtime, &config)?;
```

#### When to add a cargo feature

All new APIs should be gated by a cargo feature so users of the crate can opt into including them in their runtime.

### `javy-cli`

The CLI for compiling JS to Wasm. This isn't intended to be a CLI that accommodates all uses for all users but rather to provide a useful base of functionality. This is kind of similar to how Wasmtime ships with a crate and a CLI and doing non-generic things with Wasmtime requires writing your own CLI around the Wasmtime crate.
Expand All @@ -301,29 +104,11 @@ You should gate your feature with a cargo feature if:

- You want to support building a Wasm module with an experimental configuration of the runtime. We do this for the event loop because the current implementation has not been thoroughly vetted. We also need a build of Javy with event loop support to run a number of web platform tests for text encoding.

### `quickjs-wasm-rs`

Provides an ergonomic API around the `quickjs-wasm-sys` crate as well as a `serde` implementations for `JSValueRef`.

#### When to add a cargo feature

You should gate your feature with a cargo feature if:

- Including your feature will materially increase the size of the produced Wasm module.

### `quickjs-wasm-sys`

A Rust wrapper around the QuickJS C library.

#### When to add a cargo feature

We do not anticipate changes to this library requiring a new cargo feature. Please reach out on Zulip or in GitHub if there is a reason to add a new cargo feature.

## NPM packages

### `javy`

A JS library providing ergonomic helpers around the lower level APIs for I/O exposed by `javy-apis`.
A JS library providing ergonomic helpers around the lower level APIs for I/O exposed by the `javy` crate.

### `javy-cli`

Expand Down
8 changes: 3 additions & 5 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,19 @@ See our [architecture document](contributing-architecture.md) for more informati

## Adding additional JS APIs

We will only add JS APIs or accept contributions that add JS APIs that are potentially useful across multiple environments and do not invoke non-[WASI](https://wasi.dev/) hostcalls. If you wish to add or use JS APIs that do not meet these criteria, please use the `quickjs-wasm-rs` crate directly. We may revisit how we support importing and exporting custom functionality from Javy once [the Component Model](https://github.com/WebAssembly/component-model) has stabilized.
We will only add JS APIs or accept contributions that add JS APIs that are potentially useful across multiple environments and do not invoke non-[WASI](https://wasi.dev/) hostcalls. If you wish to add or use JS APIs that do not meet these criteria, please use the `rquickjs` crate directly. We may revisit how we support importing and exporting custom functionality from Javy once [the Component Model](https://github.com/WebAssembly/component-model) has stabilized.

## Versioning for library crates

The library crates, `javy`, `javy-apis`, `quickjs-wasm-rs`, and `quickjs-wasm-sys`, use the versioning system described in [Rust for Rustaceans](https://rust-for-rustaceans.com/) in the _Unreleased Versions_ section in the _Project Structure_ chapter. The underlying motivation is that the version in the crate's `Cargo.toml` is important between releases to ensure Cargo does not reuse a stale version if a project relies on a version of the crate that has not yet been published to crates.io and the version required by that project is updated to a version with new additive or breaking changes.

The versions for `javy` and `javy-apis` must always be the same. So a version change in the one crate should result in a version change in the other crate.
The library crate, `javy`, uses the versioning system described in [Rust for Rustaceans](https://rust-for-rustaceans.com/) in the _Unreleased Versions_ section in the _Project Structure_ chapter. The underlying motivation is that the version in the crate's `Cargo.toml` is important between releases to ensure Cargo does not reuse a stale version if a project relies on a version of the crate that has not yet been published to crates.io and the version required by that project is updated to a version with new additive or breaking changes.

### The system

After publishing a release, immediately update the version number to the next patch version with an `-alpha.1` suffix. The first time an additive change is made, reset the patch version to `0` and increment the minor version and reset the suffix to `-alpha.1`. When making additional additive changes, increment the number in the suffix, for example `-alpha.2`. The first time a breaking change is made, reset the patch version and minor version to `0` and increment the major version and reset the suffix to `-alpha.1`. When making additional breaking changes, increment the number in the suffix, for example `-alpha.2`.

When releasing, remove the suffix and then publish.

For example, let's say the last published version of `quickjs-wasm-rs` is `2.0.0`, so the current version in the Cargo.toml file is `2.0.0-alpha.1`. If you add a new public function, you would change the version to `2.1.0-alpha.1`. This is because adding a new public function is considered an additive change. After merging those changes, if you add a new public function, you would change the version to `2.1.0-alpha.2`. This is because adding another new function is an additional additive change. Now if you were to make a function that was public, private, you would change the version to `3.0.0-alpha.1`. This is because removing a public function is considered a breaking change. After merging that change, if you were to then add a new public function, then you would increment the version to `3.0.0-alpha.2` because this is making an additional additive change. It's not necessary to increment the minor version in this case because version `3.0.0` has not been published yet so version `3.0.0` can contain a mixture of additive and breaking changes from the last `2.x.x` version published.
For example, let's say the last published version of `javy` is `2.0.0`, so the current version in the Cargo.toml file is `2.0.0-alpha.1`. If you add a new public function, you would change the version to `2.1.0-alpha.1`. This is because adding a new public function is considered an additive change. After merging those changes, if you add a new public function, you would change the version to `2.1.0-alpha.2`. This is because adding another new function is an additional additive change. Now if you were to make a function that was public, private, you would change the version to `3.0.0-alpha.1`. This is because removing a public function is considered a breaking change. After merging that change, if you were to then add a new public function, then you would increment the version to `3.0.0-alpha.2` because this is making an additional additive change. It's not necessary to increment the minor version in this case because version `3.0.0` has not been published yet so version `3.0.0` can contain a mixture of additive and breaking changes from the last `2.x.x` version published.

## cargo vet

Expand Down
52 changes: 2 additions & 50 deletions docs/extending.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Extending Javy

If you want to use Javy for your own project, you may find that the existing code is not sufficient since you may want to offer custom APIs or use different branding for the CLI. The approach we'd recommend taking is to create your own version of the `javy-cli` and `javy-core` crates (you could fork these if you would like) and depend on the upstream version of the `javy` and `javy-apis` crates. You can add your own implementations of custom JS APIs in your fork of the `javy-core` crate or in a different crate that you depend on in your `javy-core` fork. If you find that something is missing in the `javy` crate that you require to implement something in your fork, we would appreciate it if you would open a GitHub issue and consider making the change upstream instead of in your fork so all users of the `javy` crate can benefit.
If you want to use Javy for your own project, you may find that the existing code is not sufficient since you may want to offer custom APIs or use different branding for the CLI. The approach we'd recommend taking is to create your own version of the `javy-cli` and `javy-core` crates (you could fork these if you would like) and depend on the upstream version of the `javy` crate. You can add your own implementations of custom JS APIs in your fork of the `javy-core` crate or in a different crate that you depend on in your `javy-core` fork. If you find that something is missing in the `javy` crate that you require to implement something in your fork, we would appreciate it if you would open a GitHub issue and consider making the change upstream instead of in your fork so all users of the `javy` crate can benefit.

See our documentation on [using complex data types in Wasm functions](complex-data-types-in-wasm-functions.md) for how to support Wasm functions that need to use byte arrays, strings, or structured data.

Expand All @@ -11,54 +11,6 @@ flowchart TD
your-cli --> wasm
subgraph wasm[your_core.wasm]
your-core --> javy[upstream javy]
your-core --> javy-apis[upstream javy-apis]
javy-apis --> javy
javy --> quickjs-wasm-rs
quickjs-wasm-rs --> quickjs-wasm-sys
javy --> rquickjs
end
```

## An example of changes you could make

Let's say you create a crate called `my-new-apis` with the following code in its `lib.rs` to define your new JS APIs:

```rust
use anyhow::Result;
use javy::Runtime;

pub fn register(runtime: &Runtime) -> Result<()> {
let ctx = runtime.context();

let global_object = ctx.global_object()?;

// Registers a `SomeApi.foo` function.
let some_api_object = ctx.object_value()?;
some_api_object.set_property("foo", ctx.wrap_callback(|_ctx, _this, _args| todo!())?)?;
global_object.set_property("SomeApi", some_api_object)?;

// Registers a `SomeOtherApi.bar` function.
let some_other_api_object = ctx.object_value()?;
some_other_api_object.set_property("bar", ctx.wrap_callback(|_ctx, _this, _args| todo!())?)?;
global_object.set_property("SomeOtherApi", some_other_api_object)?;

Ok(())
}

```

We'd suggest registering your additional API code next to where you register the Javy APIs. For example, in Javy's core crate, it's in `crates/core/runtimes.rs`:

```diff
pub(crate) fn new_runtime() -> Result<Runtime> {
let mut api_config = APIConfig::default();
api_config.log_stream(LogStream::StdErr);
- Runtime::new_with_apis(Config::default(), api_config)
+ // Creates the Javy runtime and registers the Javy APIs.
+ let runtime = Runtime::new_with_apis(Config::default(), api_config)?;
+ // Registers your custom APIs.
+ my_new_apis::register(&runtime)?;
+ Ok(runtime)
}
```

You are free to call `Runtime::new_with_apis` or another function to create your Javy `Runtime` and define and register your additional APIs wherever you would like in your own crate. This is just an example of one way you could do it.
15 changes: 0 additions & 15 deletions supply-chain/audits.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,6 @@ who = "Jeff Charles <jeff.charles@shopify.com>"
criteria = "safe-to-deploy"
version = "1.0.0"

[[audits.javy-apis]]
who = "Jeff Charles <jeff.charles@shopify.com>"
criteria = "safe-to-deploy"
version = "1.0.0"

[[audits.quickjs-wasm-rs]]
who = "Jeff Charles <jeff.charles@shopify.com>"
criteria = "safe-to-deploy"
version = "1.0.0"

[[audits.quickjs-wasm-sys]]
who = "Jeff Charles <jeff.charles@shopify.com>"
criteria = "safe-to-deploy"
version = "1.0.0"

[[trusted.aho-corasick]]
criteria = "safe-to-deploy"
user-id = 189 # Andrew Gallant (BurntSushi)
Expand Down

0 comments on commit 0a5972a

Please sign in to comment.