flujs 0.1.0+1 copy "flujs: ^0.1.0+1" to clipboard
flujs: ^0.1.0+1 copied to clipboard

flutter binding js engine.

flujs #

简体中文

js engine binding for flutter, powered by JavaScriptCore and QuickJS.

js engine android iOS windows linux darwin web
QuickJS 🔳
JavaScriptCore 🔳 🔳 🔳 🔳

✅ : Support

🔳 : InProgress

features #

  • easily to use: js call native or native call js.
  • supply xhr、promis、fetch、setTimeout、setInterval、console intrinsic functions.
  • friendly dart api.
  • support multiple JSRuntime concurrently running.

Getting Started #

add flujsflujs_jsc or flujs_qjs dependences

flutter pub add flujs flujs_qjs flujs_jsc

1、get JSFRuntime

import 'package:flujs_qjs/qjs.dart' as qjs;
import 'package:flujs_jsc/jsc.dart' as jsc;

var rt = qjs.getJSFRuntime(); 

get different js engine implement as needed.

the jsc engine's advantage is mature

the qjs engine's advantage is small size

2、get JSFContext

var ctx = rt.newContext();

3、load intrinsic functions.

ctx.loadExtension();

requires an explicit call. you can also load different built-in functions on demand.

4、execute js code

var res = ctx.eval('''eval('2+2')''');
print(res.toString());

currently only support gloabl variable、global function.

5、js call native

// common function
ctx.addInterface('run', (args) {
  debugPrint('[native] $args');
  return 1;
});
// async function
ctx.addInterface('runAsync1', (args) async {
  var completer = Completer<int>();
  Timer(const Duration(seconds: 1), () {
    completer.complete(1);
  });
  return completer.future;
});
ctx.addInterface('runAsync2', (args) async {
  var r = await asyn2(1);
  return r;
});

Future<int> asyn2(int delaySecs) async {
  var completer = Completer<int>();
  Timer(const Duration(seconds: delaySecs), () {
    completer.complete(1);
  });
  return completer.future;
}

var res = ctx.eval('''
run('from js', 2);
runAsync1().then(() => runAsync2()).then(console.log);
3;
''');
debugPrint('runAsync: $res');

6、native call js

function injs(time) {
  console.log('injs -> ', time);
}
var injs = ctx.globalObject.getProperty('injs');
assert(injs.isFunction());
injs.callAsFunction(arguments: [
  JSFValueFactory.makeNumber(ctx, id)
]);

get global variable、global function in js with context.globalContext.

if you want to isolate environment, just create different JSFContext

7、custom extension

class CustomExtension extends JSFExtension {}

the main purpose of extensions is to inject custom logic in advance in the context.

built-in xhr relies on the http library, and supports custom HttpClient so that other capabilities such as http cache, rpc, etc. can be implemented

class DioAdapter extends http.BaseClient {
  final dio = Dio();

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    final response = await dio.requestUri(
      request.url,
      options: Options(headers: request.headers),
    );
    return http.StreamedResponse(response.data, response.statusCode ?? 400);
  }
}


class LoggingInterceptor implements InterceptorContract {
  @override
  Future<http.BaseRequest> interceptRequest({
    required http.BaseRequest request,
  }) {
    print('[Logging] 😯 request: make cache or something.');
    return Future.value(request);
  }

  @override
  Future<http.BaseResponse> interceptResponse({
    required http.BaseResponse response,
  }) {
    print('[Logging] 😊 response: make cache or something.');
    return Future.value(response);
  }

  @override
  Future<bool> shouldInterceptRequest() {
    return Future.value(true);
  }

  @override
  Future<bool> shouldInterceptResponse() {
    return Future.value(true);
  }
}

var ctx = rt.newContext()..loadExtension(xhr: client == null);
final client = InterceptedClient.build(
  interceptors: [LoggingInterceptor()],
);
final client = DioAdapter();
final client = HttpPackageAdapter();
ctx.extension
          ?.chain(XhrExtension(ctx, client_: client).chain(FetchExtension(ctx)))
          .load();

Debug #

you can use your own compiled qjs engine for debugging by manually setting dynamic library files or custom environment variables

# Android
replace libqjs.so in the /flujs_qjs/android/src/main/jniLibs directory
or
specify flujs.qjs_baseUrl=https://xxxx in the gradle.properties configuration file or environment variables, and libqjs.so package it as qjs_android_[abi].tar.gz
# iOS & macOS
copy libqjs.dylib to the /flujs_qjs/[ios|macos]/Frameworks/ directory
or
set QJS_BASEURL=https://xxx, libqjs.dylib to be packaged as qjs_iphoneos.tar.gz/qjs_macosx.tar.gz
# windows & linux
set QJS_BASEURL=https://xxx, libqjs.dll/libqjs.so to package as qjs_linux_abi.tar.gz/qjs_mingw_abi.tar.gz

specify the environment variable QJS_FORCE_DOWNLOAD=true to force the download from the remote end and not read the local cache dynamic library

quickJS Cross-Compilation Reference Documentation [XMAKE QuickJS-ng Cross-Compilation Practices] (https://blog.dang8080.cn/2024/11/17/09/)

Work In Progress #

  • ✅ esm module support.
  • ✅ arrayBuffer/Blob support.fetch multiple response type support.

Known Bugs #

  • ✅ toDart isArray cause crash.
  • ✅ QJS xhr addInterface decode HttpHeader failed.
  • ✅ QJS JSContext.create(rt,intrinsic: true) will cause crash.

The Giants #

0
likes
130
points
4
downloads

Publisher

verified publisherhumphreyd.cn

Weekly Downloads

flutter binding js engine.

Homepage

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

ffi, flutter, http, http_parser, path

More

Packages that depend on flujs