Skip to content

Commit

Permalink
docs: fix end-to-end example (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored Feb 28, 2024
1 parent d5def85 commit 2ac8263
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 41 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

## Overview

JCO provides a fully native JS toolchain for working with [WebAssembly Components](https://github.com/WebAssembly/component-model) in JavaScript.
Jco provides a fully native JS toolchain for working with [WebAssembly Components](https://github.com/WebAssembly/component-model) in JavaScript.

Features include:

Expand All @@ -40,7 +40,7 @@ For creating components in other languages, see the [Cargo Component](https://gi
npm install @bytecodealliance/jco
```

JCO can be used as either a library import or as a CLI via the `jco` command.
Jco can be used as either a library import or as a CLI via the `jco` command.

## Example

Expand Down
178 changes: 141 additions & 37 deletions docs/src/example.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,192 @@
## jco Example Workflow
## Jco Example Workflow

Given an existing Wasm Component, `jco` provides the tooling necessary to work with this Component fully natively in JS.

For an example, consider a Component `cowsay.wasm`:
Jco also provides an experimental feature for generating components from JavaScript by wrapping [ComponentizeJS](https://github.com/bytecodealliance/ComponentizeJS) in the `jco componentize` command.

To demonstrate a full end-to-end component, we can create a JavaScript component embedding Spidermnokey then run it in JavaScript.

### Installing Jco

Either install Jco globally:

```shell
- cowsay.wasm
$ npm install -g @bytecodealliance/jco
$ jco --version
1.0.3
```

Where we would like to use and run this Component in a JS environment.
Or install it locally and use `npx` to run it:

### Inspecting Component WIT
```shell
$ npm install @bytecodealliance/jco
$ npx jco --version
1.0.3
```

As a first step, we might like to look instead this binary black box of a Component and see what it actually does.
Local usage can be preferable to ensure the project is reproducible and self-contained, but requires
replacing all `jco` shell calls in the following example with either `./node_modules/.bin/jco` or `npx jco`.

### Installing ComponentizeJS

To do this, we can use `jco wit` to extract the "WIT world" of the Component ([WIT](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) is the typing language used for defining Components).
To use ComponentizeJS, it must be separately installed, globally or locally depending on whether Jco was installed globally or locally. Globally:

```shell
> jco wit cowsay.wasm
$ npm install -g @bytecodealliance/componentize-js
```

Or locally:

```shell
$ npm install @bytecodealliance/componentize-js
```

Now the `jco componentize` command will be ready to use.

### Creating a Component with ComponentizeJS

This Cowsay component uses the following WIT file ([WIT](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md) is the typing language used for defining Components):

cowsay.wit
```wit
package local:cowsay;
world cowsay {
export cow: interface {
enum cows {
default,
cheese,
daemon,
dragon-and-cow,
dragon,
elephant-in-snake,
elephant,
eyes,
flaming-sheep
owl
}

say: func(text: string, cow: option<cows>) -> string;
}
}
```

From the above we can see that this Component exports a `cow` interface with a single function export, `say`, taking as input a string, an optional cow, and returning a string.
We can implement this with the following JS:

cowsay.js
```js
export const cow = {
say (text, cow = 'default') {
switch (cow) {
case 'default':
return `${text}
\\ ^__^
\\ (oo)\\______
(__)\\ )\/\\
||----w |
|| ||
`;
case 'owl':
return `${text}
___
(o o)
( V )
/--m-m-
`;
}
}
};
```

To turn this into a component run:

```shell
$ jco componentize cowsay.js --wit cowsay.wit -o cowsay.wasm

OK Successfully written cowsay.wasm with imports ().
```

> Note: For debugging, it is useful to pass `--enable-stdout` to ComponentizeJS to get error messages and enable `console.log`.
Alternatively `jco print cowsay.wasm -o out.wat` would output the full concrete Wasm WAT to inspect the Component, with all the implementation details (don't forget the `-o` flag...).
### Inspecting Component WIT

As a first step, we might like to look instead this binary black box of a Component and see what it actually does.

```shell
$ jco wit cowsay.wasm
package root:component;

world root {
export cow: interface {
enum cows {
default,
owl,
}

say: func(text: string, cow: option<cows>) -> string;
}
}
```

### Transpiling to JS

To execute the Component in a JS environment, use the `jco transpile` command to generate the JS for the Component:

```shell
> jco transpile cowsay.wasm --minify -o cowsay
$ jco transpile cowsay.wasm -o cowsay

Transpiled JS Component Files:

- cowsay/cowsay.core.wasm 2.01 MiB
- cowsay/cowsay.d.ts 0.73 KiB
- cowsay/cowsay.js 6.01 KiB
- cowsay/cowsay.core.wasm 7.61 MiB
- cowsay/cowsay.d.ts 0.07 KiB
- cowsay/cowsay.js 2.62 KiB
- cowsay/interfaces/cow.d.ts 0.21 KiB
```

Now the Component can be directly imported and used as an ES module:

test.mjs
test.js
```js
import { cow } from './cowsay/cowsay.js';

console.log(cow.say('Hello Wasm Components!'));
```

The above JavaScript can be executed in Node.js:
For Node.js to allow us to run native ES modules, we must first create or edit the local `package.json` file to include a `"type": "module"` field:

package.json
```json
{
"type": "module"
}
```

The above JavaScript can now be executed in Node.js:

```shell
$ node test.js

Hello Wasm Components!
\ ^__^
\ (oo)\______
(__)\ )/\
||----w |
|| ||
```

Passing in the optional second parameter, we can change the cow:

test.js
```js
import { cow } from './cowsay/cowsay.js';

console.log(cow.say('Hello Wasm Components!', 'owl'));
```

```shell
> node test.mjs

________________________
< Hello Wasm Components! >
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
$ node test.js

Hello Wasm Components!
___
(o o)
( V )
/--m-m-
```

Or it can be executed in a browser via a module script:
It can also be executed in a browser via a module script:

```html
<script type="module" src="test.mjs"></script>
<script type="module" src="test.js"></script>
```

There are a number of custom transpilation options available, detailed in the [API section](README.md#API).
4 changes: 2 additions & 2 deletions docs/src/transpiling.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ export const iface = {
### WASI Proposals

**JCO will always take PRs to support all open WASI proposals.**
**Jco will always take PRs to support all open WASI proposals.**

These PRs can be implemented by extending the [default map configuration provided by JCO](https://github.com/bytecodealliance/jco/blob/main/src/cmd/transpile.js#L110) to support the new `--map wasi:subsytem/*=shimpkg/subsystem#*` for the WASI subsystem being implemented.
These PRs can be implemented by extending the [default map configuration provided by Jco](https://github.com/bytecodealliance/jco/blob/main/src/cmd/transpile.js#L110) to support the new `--map wasi:subsytem/*=shimpkg/subsystem#*` for the WASI subsystem being implemented.

> `shimpkg` in the above refers to a published npm package implementation to install per JS ecosystem conventions. This way, polyfill packages can be published to npm.
Expand Down

0 comments on commit 2ac8263

Please sign in to comment.