quickjs 0.15.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CLAUDE.md +8 -0
- data/README.md +20 -0
- data/ext/quickjsrb/quickjsrb.c +92 -9
- data/ext/quickjsrb/quickjsrb.h +4 -0
- data/lib/quickjs/version.rb +1 -1
- data/polyfills/package-lock.json +2 -2
- data/polyfills/package.json +1 -1
- data/sig/quickjs.rbs +5 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e8f0513a5e0a54c24bdc8c20000f8cf9d1611e394683a710a2260ebedb57913a
|
|
4
|
+
data.tar.gz: 335ab02e13a0b8b137342f5895bc821641ac33d6f8370e05b9c24f0ca3ce7416
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6e975069d7b77adf0d2e4ca7d6459c6499e1465155cae563ff3fb87d7f1f8aefe9f3b26191d9736a2c398ee13df48d5ded389b17f54aec56dc2bf4e4ac580a8
|
|
7
|
+
data.tar.gz: b1341d5c2e0ba51e6891752bc6c203fe7c2bfc8e1d76beafaf93129f3fb0bb600c64e786beec42ec6a9cc95336562de739d32f02711422b1f9930dd4b57cbb93
|
data/CLAUDE.md
CHANGED
|
@@ -55,6 +55,14 @@ Tests use minitest with `describe`/`it` blocks. Key test files:
|
|
|
55
55
|
- `test/quickjs_test.rb` — Main test suite (value conversion, errors, VM features, ESM imports, function definitions)
|
|
56
56
|
- `test/quickjs_polyfill_test.rb` — Intl polyfill tests
|
|
57
57
|
|
|
58
|
+
## Release Process
|
|
59
|
+
|
|
60
|
+
1. On `main`, run `bundle exec rake polyfills:build` — rebuilds polyfill bundles and syncs `polyfills/package.json` version to match the gem version
|
|
61
|
+
2. Bump `lib/quickjs/version.rb` and `Gemfile.lock` to the new version
|
|
62
|
+
3. Commit `lib/quickjs/version.rb`, `Gemfile.lock`, `polyfills/package.json`, `polyfills/package-lock.json` as `"prepare vX.Y.Z"` — do NOT push; `rake release` handles that
|
|
63
|
+
4. Run `bundle exec rake release` — tags, pushes to GitHub, and publishes to RubyGems (human step; do not run this as Claude)
|
|
64
|
+
5. Create a GitHub release via `gh release create` with notes following the pattern of previous releases
|
|
65
|
+
|
|
58
66
|
## Build Notes
|
|
59
67
|
|
|
60
68
|
- `extconf.rb` compiles with `-DNDEBUG` to avoid conflicts with Ruby 4.0 GC assertions
|
data/README.md
CHANGED
|
@@ -129,6 +129,26 @@ vm.import('DefaultExport', from: File.read('exports.esm.js'))
|
|
|
129
129
|
vm.import('* as all', from: File.read('exports.esm.js'))
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
+
#### `Quickjs::VM#module_loader=`: 🧩 Resolve `import` specifiers from Ruby
|
|
133
|
+
|
|
134
|
+
By default, `import` specifiers that aren't already loaded fall through to QuickJS's filesystem loader. Set a `module_loader` Proc to resolve specifiers in-memory instead — useful when the source code lives in a database, an importmap, or a virtual filesystem.
|
|
135
|
+
|
|
136
|
+
```rb
|
|
137
|
+
vm = Quickjs::VM.new
|
|
138
|
+
modules = {
|
|
139
|
+
'a' => "import { b } from 'b'; export const a = () => `a-${b()}`;",
|
|
140
|
+
'b' => "export const b = () => 'b-result';"
|
|
141
|
+
}
|
|
142
|
+
vm.module_loader = ->(name) { modules[name] }
|
|
143
|
+
|
|
144
|
+
vm.import(['a'], filename: 'a')
|
|
145
|
+
vm.eval_code('a()') #=> 'a-b-result'
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The Proc receives the (already normalized) module specifier and returns the module source as a `String`, or `nil` to signal "not found" (which raises `Quickjs::ReferenceError` on the JS side). Pass `nil` to clear a previously set loader.
|
|
149
|
+
|
|
150
|
+
When `module_loader=` is set, pass `filename:` to `import` instead of `from:` to resolve a named specifier directly through the loader — no inline bridge source needed.
|
|
151
|
+
|
|
132
152
|
#### `Quickjs::VM#define_function`: 💎 Define a global function for JS by Ruby
|
|
133
153
|
|
|
134
154
|
```rb
|
data/ext/quickjsrb/quickjsrb.c
CHANGED
|
@@ -470,6 +470,59 @@ VALUE to_rb_value(JSContext *ctx, JSValue j_val)
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
struct module_loader_call_args
|
|
474
|
+
{
|
|
475
|
+
VALUE proc;
|
|
476
|
+
VALUE r_module_name;
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
static VALUE r_module_loader_call(VALUE r_args_val)
|
|
480
|
+
{
|
|
481
|
+
struct module_loader_call_args *args = (struct module_loader_call_args *)r_args_val;
|
|
482
|
+
return rb_funcall(args->proc, rb_intern("call"), 1, args->r_module_name);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
static JSModuleDef *quickjsrb_module_loader(JSContext *ctx, const char *module_name, void *opaque, JSValueConst attributes)
|
|
486
|
+
{
|
|
487
|
+
VMData *data = JS_GetContextOpaque(ctx);
|
|
488
|
+
if (NIL_P(data->module_loader))
|
|
489
|
+
return js_module_loader(ctx, module_name, opaque, attributes);
|
|
490
|
+
|
|
491
|
+
struct module_loader_call_args args = {data->module_loader, rb_str_new_cstr(module_name)};
|
|
492
|
+
int state;
|
|
493
|
+
VALUE r_source = rb_protect(r_module_loader_call, (VALUE)&args, &state);
|
|
494
|
+
if (state)
|
|
495
|
+
{
|
|
496
|
+
VALUE r_error = rb_errinfo();
|
|
497
|
+
rb_set_errinfo(Qnil);
|
|
498
|
+
JSValue j_error = j_error_from_ruby_error(ctx, r_error);
|
|
499
|
+
JS_Throw(ctx, j_error);
|
|
500
|
+
return NULL;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (NIL_P(r_source) || r_source == Qfalse)
|
|
504
|
+
{
|
|
505
|
+
JS_ThrowReferenceError(ctx, "module loader returned no source for '%s'", module_name);
|
|
506
|
+
return NULL;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (!RB_TYPE_P(r_source, T_STRING))
|
|
510
|
+
{
|
|
511
|
+
JS_ThrowTypeError(ctx, "module loader must return a String or nil, got %s", rb_obj_classname(r_source));
|
|
512
|
+
return NULL;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
JSValue j_func = JS_Eval(ctx, RSTRING_PTR(r_source), RSTRING_LEN(r_source), module_name,
|
|
516
|
+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
|
517
|
+
if (JS_IsException(j_func))
|
|
518
|
+
return NULL;
|
|
519
|
+
|
|
520
|
+
js_module_set_import_meta(ctx, j_func, FALSE, FALSE);
|
|
521
|
+
JSModuleDef *m = JS_VALUE_GET_PTR(j_func);
|
|
522
|
+
JS_FreeValue(ctx, j_func);
|
|
523
|
+
return m;
|
|
524
|
+
}
|
|
525
|
+
|
|
473
526
|
static VALUE r_try_call_proc(VALUE r_try_args)
|
|
474
527
|
{
|
|
475
528
|
return rb_funcall(
|
|
@@ -732,7 +785,7 @@ static VALUE vm_m_initialize(int argc, VALUE *argv, VALUE r_self)
|
|
|
732
785
|
JS_SetMemoryLimit(runtime, NUM2UINT(r_memory_limit));
|
|
733
786
|
JS_SetMaxStackSize(runtime, NUM2UINT(r_max_stack_size));
|
|
734
787
|
|
|
735
|
-
JS_SetModuleLoaderFunc2(runtime, NULL,
|
|
788
|
+
JS_SetModuleLoaderFunc2(runtime, NULL, quickjsrb_module_loader, js_module_check_attributes, NULL);
|
|
736
789
|
js_std_init_handlers(runtime);
|
|
737
790
|
|
|
738
791
|
JSValue j_global = JS_GetGlobalObject(data->context);
|
|
@@ -1143,6 +1196,25 @@ static VALUE vm_m_callGlobalFunction(int argc, VALUE *argv, VALUE r_self)
|
|
|
1143
1196
|
return to_rb_return_value(data->context, js_std_await(data->context, j_result));
|
|
1144
1197
|
}
|
|
1145
1198
|
|
|
1199
|
+
static VALUE vm_m_set_module_loader(VALUE r_self, VALUE r_loader)
|
|
1200
|
+
{
|
|
1201
|
+
VMData *data;
|
|
1202
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
1203
|
+
|
|
1204
|
+
if (!NIL_P(r_loader) && !rb_obj_is_kind_of(r_loader, rb_cProc))
|
|
1205
|
+
rb_raise(rb_eTypeError, "module_loader must be a Proc or nil");
|
|
1206
|
+
|
|
1207
|
+
data->module_loader = r_loader;
|
|
1208
|
+
return r_loader;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
static VALUE vm_m_get_module_loader(VALUE r_self)
|
|
1212
|
+
{
|
|
1213
|
+
VMData *data;
|
|
1214
|
+
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
1215
|
+
return data->module_loader;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1146
1218
|
static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
|
|
1147
1219
|
{
|
|
1148
1220
|
VALUE r_import_string, r_opts;
|
|
@@ -1150,7 +1222,8 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
|
|
|
1150
1222
|
if (NIL_P(r_opts))
|
|
1151
1223
|
r_opts = rb_hash_new();
|
|
1152
1224
|
VALUE r_from = rb_hash_aref(r_opts, ID2SYM(rb_intern("from")));
|
|
1153
|
-
|
|
1225
|
+
VALUE r_filename = rb_hash_aref(r_opts, ID2SYM(rb_intern("filename")));
|
|
1226
|
+
if (NIL_P(r_from) && NIL_P(r_filename))
|
|
1154
1227
|
{
|
|
1155
1228
|
VALUE r_error_message = rb_str_new2("missing import source");
|
|
1156
1229
|
rb_exc_raise(rb_funcall(QUICKJSRB_ERROR_FOR(QUICKJSRB_ROOT_RUNTIME_ERROR), rb_intern("new"), 2, r_error_message, Qnil));
|
|
@@ -1161,16 +1234,24 @@ static VALUE vm_m_import(int argc, VALUE *argv, VALUE r_self)
|
|
|
1161
1234
|
VMData *data;
|
|
1162
1235
|
TypedData_Get_Struct(r_self, VMData, &vm_type, data);
|
|
1163
1236
|
|
|
1164
|
-
char *filename
|
|
1165
|
-
|
|
1166
|
-
JSValue module = JS_Eval(data->context, source, strlen(source), filename, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
|
1167
|
-
if (JS_IsException(module))
|
|
1237
|
+
char *filename;
|
|
1238
|
+
if (!NIL_P(r_filename))
|
|
1168
1239
|
{
|
|
1240
|
+
filename = StringValueCStr(r_filename);
|
|
1241
|
+
}
|
|
1242
|
+
else
|
|
1243
|
+
{
|
|
1244
|
+
filename = random_string();
|
|
1245
|
+
char *source = StringValueCStr(r_from);
|
|
1246
|
+
JSValue module = JS_Eval(data->context, source, strlen(source), filename, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
|
1247
|
+
if (JS_IsException(module))
|
|
1248
|
+
{
|
|
1249
|
+
JS_FreeValue(data->context, module);
|
|
1250
|
+
return to_rb_value(data->context, module);
|
|
1251
|
+
}
|
|
1252
|
+
js_module_set_import_meta(data->context, module, TRUE, FALSE);
|
|
1169
1253
|
JS_FreeValue(data->context, module);
|
|
1170
|
-
return to_rb_value(data->context, module);
|
|
1171
1254
|
}
|
|
1172
|
-
js_module_set_import_meta(data->context, module, TRUE, FALSE);
|
|
1173
|
-
JS_FreeValue(data->context, module);
|
|
1174
1255
|
|
|
1175
1256
|
VALUE r_import_settings = rb_funcall(
|
|
1176
1257
|
rb_const_get(rb_cClass, rb_intern("Quickjs")),
|
|
@@ -1219,6 +1300,8 @@ RUBY_FUNC_EXPORTED void Init_quickjsrb(void)
|
|
|
1219
1300
|
rb_define_method(r_class_vm, "call", vm_m_callGlobalFunction, -1);
|
|
1220
1301
|
rb_define_method(r_class_vm, "define_function", vm_m_defineGlobalFunction, -1);
|
|
1221
1302
|
rb_define_method(r_class_vm, "import", vm_m_import, -1);
|
|
1303
|
+
rb_define_method(r_class_vm, "module_loader", vm_m_get_module_loader, 0);
|
|
1304
|
+
rb_define_method(r_class_vm, "module_loader=", vm_m_set_module_loader, 1);
|
|
1222
1305
|
rb_define_method(r_class_vm, "on_log", vm_m_on_log, 0);
|
|
1223
1306
|
r_define_log_class(r_class_vm);
|
|
1224
1307
|
}
|
data/ext/quickjsrb/quickjsrb.h
CHANGED
|
@@ -54,6 +54,7 @@ typedef struct VMData
|
|
|
54
54
|
struct EvalTime *eval_time;
|
|
55
55
|
VALUE log_listener;
|
|
56
56
|
VALUE alive_objects;
|
|
57
|
+
VALUE module_loader;
|
|
57
58
|
JSValue j_file_proxy_creator;
|
|
58
59
|
} VMData;
|
|
59
60
|
|
|
@@ -85,6 +86,7 @@ static void vm_mark(void *ptr)
|
|
|
85
86
|
rb_gc_mark_movable(data->defined_functions);
|
|
86
87
|
rb_gc_mark_movable(data->log_listener);
|
|
87
88
|
rb_gc_mark_movable(data->alive_objects);
|
|
89
|
+
rb_gc_mark_movable(data->module_loader);
|
|
88
90
|
}
|
|
89
91
|
|
|
90
92
|
static void vm_compact(void *ptr)
|
|
@@ -93,6 +95,7 @@ static void vm_compact(void *ptr)
|
|
|
93
95
|
data->defined_functions = rb_gc_location(data->defined_functions);
|
|
94
96
|
data->log_listener = rb_gc_location(data->log_listener);
|
|
95
97
|
data->alive_objects = rb_gc_location(data->alive_objects);
|
|
98
|
+
data->module_loader = rb_gc_location(data->module_loader);
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
static const rb_data_type_t vm_type = {
|
|
@@ -113,6 +116,7 @@ static VALUE vm_alloc(VALUE r_self)
|
|
|
113
116
|
data->defined_functions = rb_hash_new();
|
|
114
117
|
data->log_listener = Qnil;
|
|
115
118
|
data->alive_objects = rb_hash_new();
|
|
119
|
+
data->module_loader = Qnil;
|
|
116
120
|
data->j_file_proxy_creator = JS_UNDEFINED;
|
|
117
121
|
|
|
118
122
|
EvalTime *eval_time = malloc(sizeof(EvalTime));
|
data/lib/quickjs/version.rb
CHANGED
data/polyfills/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quickjs-rb-polyfills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "quickjs-rb-polyfills",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.16.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@formatjs/intl-datetimeformat": "^7.3.1",
|
|
12
12
|
"@formatjs/intl-getcanonicallocales": "^3.2.2",
|
data/polyfills/package.json
CHANGED
data/sig/quickjs.rbs
CHANGED
|
@@ -29,6 +29,11 @@ module Quickjs
|
|
|
29
29
|
| (Array[String | Symbol] path, *Symbol flags) { (*untyped) -> untyped } -> Array[Symbol]
|
|
30
30
|
|
|
31
31
|
def import: (String | Array[String] | Hash[Symbol, String] imported, from: String, ?code_to_expose: String?) -> true
|
|
32
|
+
| (String | Array[String] | Hash[Symbol, String] imported, filename: String) -> true
|
|
33
|
+
|
|
34
|
+
def module_loader: () -> (^(String) -> String? | nil)
|
|
35
|
+
|
|
36
|
+
def module_loader=: (^(String) -> String? | nil) -> (^(String) -> String? | nil)
|
|
32
37
|
|
|
33
38
|
def on_log: () { (Log) -> void } -> nil
|
|
34
39
|
|