| Crates.io | wasm-rquickjs-cli |
| lib.rs | wasm-rquickjs-cli |
| version | 0.0.17 |
| created_at | 2025-06-30 04:59:39.5981+00 |
| updated_at | 2025-09-23 15:14:32.996667+00 |
| description | Tool for wrapping JavaScript modules as WebAssembly components using the QuickJS engine |
| homepage | https://github.com/golemcloud/wasm-rquickjs |
| repository | https://github.com/golemcloud/wasm-rquickjs |
| max_upload_size | |
| id | 1731442 |
| size | 733,702 |
Command line tool and library to generate a Rust crate wrapping JavaScript code into a WebAssembly Component using the QuickJS engine.
ComponentizeJS achieves the same goal of wrapping JavaScript code into a WebAssembly Component, but it does it using a modified version of the SpiderMonkey engine.
Advantages of wasm-rquickjs over ComponentizeJS:
Advantages of ComponentizeJS over wasm-rquickjs:
The project is similar to wasmedge-quickjs in using te QuickJS engine compiled to WASM to run JavaScript code, but it is different in the following ways:
The tool can be used as a command line tool or as a library. The command line tool has two top level commands:
generate-wrapper-crate Generate the wrapper crate for a JavaScript module
generate-dts Generate TypeScript module definitions
This is the primary command that generates the Rust crate embedding the JavaScript code into a WebAssembly Component.
Usage: wasm-rquickjs generate-wrapper-crate --js <JS> --wit <WIT> --output <OUTPUT>
--js arguments is the path to the JavaScript file to be wrapped. There can be only one JavaScript file,
containing an ES6 module exporting the necessary functions and classes as described below.--wit argument is the path to the WIT root containing a single world that describes the imports and exports of
the component--output argument is the path to the output directory where the generated Rust crate will be created.The output directory is going to contain a self-contained Rust crate that can be compiled into a WASM component using the cargo-component tool.
The generated crate has some features that control what imports the component will have beside the ones defined in the user's WIT world:
logging: enables the wasi:logging import to be used for the JavaScript console APIhttp: enables the wasi:http import to be used for the JavaScript fetch APIBy default both feature flags are enabled.
The generate-dts command generates TypeScript module definitions for all the exported and imported interfaces:
Usage: wasm-rquickjs generate-dts --wit <WIT> --output <OUTPUT>
--wit argument is the path to the WIT root containing a single world that describes the imports and exports of
the component.--output argument is the path to the output directory where the generated TypeScript module definitions (
.d.ts) will be created.wasm-rquickjs is integrated into Golem's command line interface, so it can be directly used
using Golem app templates.
The following WIT code:
package demo:pkg;
world example {
export hello: func() -> string;
}
must be implemented in JavaScript as:
export const hello = () => {
return "Hello, world!";
};
The this is bound to the module object. The JS function name is always in camelCase.
Exported interfaces has to be exported from JavaScript as objects:
The following WIT example:
package demo:pkg;
interface sample-api {
get-string-length: func(value: string) -> u64;
}
world example {
export sample-api;
}
has to be implemented in JavaScript as:
export const sampleApi = {
getStringLength: (value) => {
return value.length;
}
};
All names are converted to camelCase. The JavaScript this is bound to object representing the exporter interface, in
the above example it is sampleApi.
Exported resources are implemented as classes in JS:
The following WIT example:
package demo:pkg;
interface iface {
resource example-resource {
constructor(name: string);
get-name: func() -> string;
compare: static func(h1: borrow<example-resource>, h2: borrow<example-resource>) -> s32;
merge: static func(h1: own<example-resource>, h2: own<example-resource>) -> hello;
}
}
world example {
export iface;
}
Must be exported from JavaScript in the following way:
class Hello {
constructor(name) {
this.name = name;
}
// async to demonstrate it is possible
async getName() {
return this.name;
}
static compare(h1, h2) {
if (h1.name === h2.name) {
return 0;
} else if h1.name < h2.name) {
return -1;
} else {
return 1;
}
}
static merge(h1, h2) {
return new Hello(`${h1.name} & ${h2.name}`);
}
}
export const iface = {
Hello: Hello,
};
The classes have a UpperCamelCase name and their methods are in camelCase. All methods and static methods can be either sync or async.
| Name | WIT | JS | Notes |
|---|---|---|---|
| Character | char |
string |
- |
| String | string |
string |
- |
| Signed 8-bit integer | s8 |
number |
- |
| Unsigned 8-bit integer | u8 |
number |
- |
| Signed 16-bit integer | s16 |
number |
- |
| Unsigned 16-bit integer | u16 |
number |
- |
| Signed 32-bit integer | s32 |
number |
- |
| Unsigned 32-bit integer | u32 |
number |
- |
| Signed 64-bit integer | s64 |
bigint |
- |
| Unsigned 64-bit integer | u64 |
bigint |
- |
| 32-bit float | f32 |
number |
- |
| 64-bit float | f64 |
number |
- |
| Optional type | option<T> |
T | undefined |
Nested options are encoded differently |
| List | list<T> |
T[] |
- |
| Result | result<T, E> |
{ tag: "ok": val: T } | { tag: "err", val: E } |
- |
| Tuple | tuple<A, B, C> |
Array | - |
| Enum | enum { a, b, c} |
"a" | "b" | "c" |
The strings match the WIT enum cases |
| Flags | flags { a, b, c } |
{ a: boolean, b: boolean, c: boolean } |
The object keys are camelCase |
| Record | record { .. } |
Object | Field names are camelCase |
| Variant | variant { .. } |
{ tag: "x", val: X } |
Tag names match the WIT variant case names; val is undefined for unit cases |
If the logging feature flag is enabled in the generated crate, it depends on wasi:logging, otherwise just on the
core WASI interfaces.
assertclearcountcountResetdebugdirdirXmlerrorgroupgroupCollapsedgroupEndinfologtabletimetimeEndtimeLogtracewarnOnly if the http feature flag is enabled in the generated crate. It depends on wasi:http.
fetchHeadersRequestResponseFormDataBlobFileURLURLSearchParamsImplemented by https://github.com/MattiasBuelens/web-streams-polyfill
ByteLengthQueuingStrategyCountQueuingStrategyReadableByteStreamControllerReadableStreamReadableStreamBYOBReaderReadableStreamBYOBRequestReadableStreamDefaultControllerReadableStreamDefaultReaderTransformStreamTransformStreamDefaultControllerWritableStreamWritableStreamDefaultControllersetTimeout
clearTimeout
setInterval
clearInterval
setImmediate
TextEncoderTextDecoderTextDecoderStreamTextEncoderStreamnode:utilformatdeprecatedebugLoginspectisArrayisBooleanisNullisNullOrUndefinedisNumberisStringisSymbolisUndefinedisRegExpisObjectisDateisErrorisFunctionisPrimitiveisBufferlog_extendpromisifycallbackifynode:bufferBufferINSPECT_MAX_BYTESkMaxLengthkStringMaxLengthconstantsSlowBuffernode:fsreadFilereadFileSyncwriteFilewriteFileSyncnode:processargvargv0envcwdbase64-jsbyteLengthtoByteArrayfromByteArrayieee754readwriteGlobal:
parseIntparseFloatisNaNisFinitequickMicrotaskdecodeURIdecodeURIComponentencodeURIencodeURIComponentescapeunescapeInfinityNaNundefined[Symbol.toStringTag]Object
creategetPrototypeOfsetPrototypeOfdefinePropertydefinePropertiesgetOwnPropertyNamesgetOwnPropertySymbolsgroupBykeysvaluesentriesisExtensiblepreventExtensionsgetOwnPropertyDescriptorgetOwnPropertyDescriptorsisassignsealfreezeisSealedisFrozenfromEntrieshasOwntoStringtoLocaleStringvalueOfhasOwnPropertyisPrototypeOfpropertyIsEnumerable__proto____defineGetter____defineSetter____lookupGetter____lookupSetter__Function
callapplybindtoString[Symbol.hasInstance]fileNamelineNumbercolumnNumberError
namemessagetoStringisErrorcaptureStackTracestackTraceLimitprepareStackTraceGenerator
nextreturnthrow[Symbol.toStringTag]fromIterator
fromdrop
filter
flatMap
map
take
every
find
forEach
some
reduce
toArray
[Symbol.iterator]
[Symbol.toStringTag]Array
isArrayfromof[Symbol.species]atwithconcateverysomeforEachmapfilterreducereduceRightfillfindfindIndexfindLastfindLastIndexindexOflastIndexOfincludesjointoStringtoLocaleStringpoppushshiftunshiftreversetoReversedsorttoSortedslicesplicetoSplicedcopyWithinflatMapflatvalues[Symbol.iterator]keysentriesNumber
parseIntparseFloatisNaNisFiniteisIntegerisSafeIntegerMAX_VALUEMIN_VALUENaNNEGATIVE_INFINITYPOSITIVE_INFINITYEPSILONMAX_SAFE_INTEGERMIN_SAFE_INTEGERtoExponentialtoFixedtoPrecisiontoStringtoLocaleStringvalueOfBoolean
toStringvalueOfString
fromCharCodefromCodePointrawlengthatcharCodeAtcharAtconcatcodePointAtisWellFormedtoWellFormedindexOflastIndexOfincludesendsWithstartsWithmatchmatchAllsearchsplitsubstringsubstrslicerepeatreplacereplaceAllpadEndpadStarttrimtrimEndtrimRighttrimStarttrimLefttoStringvalueOflocaleComparenormalizetoLowerCasetoUpperCasetoLocaleLowerCasetoLocaleUpperCase[Symbol.iterator]anchorbigblinkboldfixedfontcolorfontsizeitalicslinksmallstrikesubsupSymbol
forkeyFortoStringvalueOfdescription[Symbol.toPrimitive][Symbol.toStringTag]Map
groupBy[Symbol.species]setgethasdeleteclearsizeforEachvalueskeysentries[Symbol.iterator][Symbol.toStringTag]Set
[Symbol.species]addhasdeleteclearsizeforEachisDisjointFromisSubsetOfisSupersetOfintersectiondifferencesymmetricDifferenceunionvalueskeys[Symbol.iterator]entries[Symbol.toStringTag]WeakMap
setgethasdelete[Symbol.toStringTag]WeakSet
addhasdelete[Symbol.toStringTag]GeneratorFunction
[Symbol.toStringTag]Math
minmaxabsfloorceilroundsqrtacosasinatanatan2cosexplogpowsintantruncsigncoshsinhtanhacoshasinhatanhexpm1log1plog2log10cbrthypotrandomf16roundfroundimulclz32sumPrecise[Symbol.toStringTag]ELN10LN2LOG2ELOG10EPISQRT1_2SQRT2Reflect
applyconstructdefinePropertydeletePropertygetgetOwnPropertyDescriptorgetPrototypeOfhasisExtensibleownKeyspreventExtensionssetsetPrototypeOf[Symbol.toStringTag]RegExp
escape[Symbol.species]flagssourceglobalignoreCasemultilinedotAllunicodeunicodeSetsstickyhasIndicesexeccompiletesttoString[Symbol.replace][Symbol.match][Symbol.matchAll][Symbol.search][Symbol.split]JSON
parsestringify[Symbol.toStringTag]Promise
resolverejectallallSettledanytryracewithResolvers[Symbol.species]thencatchfinally[Symbol.toStringTag]AsyncFunction
[Symbol.toStringTag]AsyncIterator
nextreturnthrowAsyncGeneratorFunction
[Symbol.toStringTag]AsyncGenerator
nextreturnthrow[Symbol.toStringTag]Date
nowparseUTCvalueOftoString[Symbol.toPrimitive]toUTCStringtoGMTStringtoISOStringtoDateStringtoTimeStringtoLocaleStringtoLocaleDateStringtoLocaleTimeStringgetTimezoneOffsetgetTimegetYeargetFullYeargetUTCFullYeargetMonthgetUTCMonthgetDategetUTCDategetHoursgetUTCHoursgetMinutesgetUTCMinutesgetSecondsgetUTCSecondsgetMillisecondsgetUTCMillisecondsgetDaygetUTCDaysetTimesetMillisecondssetUTCMillisecondssetSecondssetUTCSecondssetMinutessetUTCMinutessetHourssetUTCHourssetDatesetUTCDatesetMonthsetUTCMonthsetYearsetFullYearsetUTCFullYeartoJSONBigInt
asIntNasUintNtoStringvalueOf[Symbol.toStringTag]ArrayBuffer
isView[Symbol.speciesbyteLengthmaxByteLengthresizeabledetachedresizeslicetransfertransferToFixedLength[Symbol.toStringTag]SharedArrayBuffer
[Symbol.species]byteLengthmaxByteLengthgrowablegrowslice[Symbol.toStringTag]Typed arrays (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array,
BigUint64Array, Float32Array, Float64Array, Float16Array)
fromof[Symbol.species]lengthatwithbufferbyteLengthsetbyteOffsetvalues[Symbol.iterator]keysentries[Symbol.toStringTag]copyWithineverysomeforEachmapfilterreducereduceRightfillfindfindIndexfindLastfindLastIndexreversetoReversedslicesubarraysorttoSortedjointoLocaleStringindexOflastIndexOfincludesDataView
bufferbyteLengthbyteOffsetgetInt8getUint8getInt16getUint16getInt32getUint32getBigInt64getBigUint64getFloat16getFloat32getFloat64setInt8setUint8setInt16setUint16setInt32setUint32setBigInt64setBigUint64setFloat16setFloat32setFloat64[Symbol.toStringTag]Atomics
addandorsubxorexchangecompareExchangeloadstoreisLockFreepausewaitnotify[Symbol.toStringTag]Performance
nowWeakRef
deref[Symbol.toStringTag]FinalizationRegistry
registerunregister[Symbol.toStringTag]Callsite
isNative
getFileNamegetFunctiongetFunctionNamegetColumnNumbergetLineNumber[Symbol.toStringTag]Proxy
There are a few important things to keep in mind when working on the project:
The skeleton crate can be opened and compiled separately when working on the APIs provided for JavaScript.
Unfortunately we cannot use the Cargo.toml file name in it because that breaks Rust packaging - so before working on
it, it has to be renamed to Cargo.toml
and before committing back it has to be renamed back to Cargo.toml_.
If the skeleton crate was compiled for testing, and then wasm-rquickjs is compiled, the include_dir! macro is
embedding everything from the skeleton directory including the target directory, resulting in slow compilation
times and huge resulting binaries. Use the cleanup-skeleton.sh script to quickly remove the target directory from
the skeleton crate.