From d7224dd84092b41b7e03b6fdb49f34b0195ff34e Mon Sep 17 00:00:00 2001 From: ekibun Date: Sat, 2 Jan 2021 11:28:58 +0800 Subject: [PATCH] update readme --- CHANGELOG.md | 4 ++ README.md | 96 +++++++++++++++++++++++++------------------- example/pubspec.lock | 2 +- lib/flutter_qjs.dart | 15 ++++--- lib/isolate.dart | 17 +++++--- pubspec.yaml | 2 +- 6 files changed, 82 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59bf684..acf702a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ * @LastEditTime: 2020-12-02 11:36:40 --> +## 0.2.1 + +* code cleanup. + ## 0.2.0 * breakdown change with new constructor. diff --git a/README.md b/README.md index 81ea6b9..390e373 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,38 @@ --> # flutter_qjs -A quickjs engine for flutter. - -## Feature - This plugin is a simple js engine for flutter using the `quickjs` project with `dart:ffi`. Plugin currently supports all the platforms except web! -Event loop of `FlutterQjs` should be implemented by calling `FlutterQjs.dispatch()`. +## Getting Started -ES6 module with `import` function is supported and can be managed in dart with `setModuleHandler`. +### Basic usage -A global function `channel` is presented to invoke dart function. Data conversion between dart and js are implemented as follow: +Firstly, create a `FlutterQjs` object, then call `dispatch` to dispatch event loop: + +```dart +final engine = FlutterQjs() +engine.dispatch(); +``` + +Use `evaluate` method to run js script, now you can use it synchronously, or use await to resolve `Promise`: + +```dart +try { + print(engine.evaluate(code ?? '')); +} catch (e) { + print(e.toString()); +} +``` + +Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`. Parameter `port` should be close to stop `dispatch` loop when you do not need it. + +```dart +engine.port.close(); // stop dispatch loop +engine.close(); // close engine +engine = null; +``` + +Data conversion between dart and js are implemented as follow: | dart | js | | --------------------------------------------------- | ------------------ | @@ -31,13 +52,13 @@ A global function `channel` is presented to invoke dart function. Data conversio | JSFunction(...args)
IsolateJSFunction(...args) | function(....args) | | Future | Promise | -**notice:** `function` can only be sent from js to dart. `IsolateJSFunction` always returns asynchronously. +**notice:** `function` can only be sent from js to dart. -## Getting Started +### Invoke dart function -### Run on main thread +A global JavaScript function `channel` is presented to invoke dart function. -1. Create a `FlutterQjs` object, pass handlers to implement js-dart interaction and resolving modules. For example, you can use `Dio` to implement http in js: +In constructor, pass handler function to manage JavaScript call. For example, you can use `Dio` to implement http in JavaScript: ```dart final engine = FlutterQjs( @@ -49,6 +70,21 @@ final engine = FlutterQjs( throw Exception("No such method"); } }, +); +``` + +then, in java script you can use channel function to invoke `methodHandler`, make sure the second parameter is a list: + +```javascript +channel("http", ["http://example.com/"]); +``` + +### Use modules + +ES6 module with `import` function is supported and can be managed in dart with `moduleHandler`: + +```dart +final engine = FlutterQjs( moduleHandler: (String module) { if(module == "hello") return "export default (name) => `hello \${name}!`;"; @@ -57,41 +93,19 @@ final engine = FlutterQjs( ); ``` -in javascript, `channel` function is equiped to invoke `methodHandler`, make sure the second parameter is a list: - -```javascript -channel("http", ["http://example.com/"]); -``` - -`import` function is used to get modules: +then in JavaScript, `import` function is used to get modules: ```javascript import("hello").then(({default: greet}) => greet("world")); ``` -**notice:** To use async function in module handler, try [Run on isolate thread](#Run-on-isolate-thread) +**notice:** Module handler should be called only once for each module name. To reset the module cache, call `FlutterQjs.close` then `evaluate` again. -2. Then call `dispatch` to dispatch event loop. - -```dart -engine.dispatch(); -``` - -1. Use `evaluate` to run js script, now you can use it synchronously, or use await to resolve `Promise`: - -```dart -try { - print(engine.evaluate(code ?? '')); -} catch (e) { - print(e.toString()); -} -``` - -1. Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`. +To use async function in module handler, try [Run on isolate thread](#Run-on-isolate-thread) ### Run on isolate thread -1. Create a `IsolateQjs` object, pass handlers to implement js-dart interaction and resolving modules. The `methodHandler` is used in isolate, so **the handler function must be a top-level function or a static method**. Async function such as `rootBundle.loadString` can be used now to get module: +Create a `IsolateQjs` object, pass handlers to implement js-dart interaction and resolving modules. The `methodHandler` is used in isolate, so **the handler function must be a top-level function or a static method**. Async function such as `rootBundle.loadString` can be used now to get modules: ```dart dynamic methodHandler(String method, List arg) { @@ -112,7 +126,7 @@ final engine = IsolateQjs( // not need engine.dispatch(); ``` -2. Same as run on main thread, use `evaluate` to run js script. In this way, `Promise` return by `evaluate` will be automatically tracked and return the resolved data: +Same as run on main thread, use `evaluate` to run js script. In this way, `Promise` return by `evaluate` will be automatically tracked and return the resolved data: ```dart try { @@ -122,11 +136,11 @@ try { } ``` -3. Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`. +Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`. [This example](example/lib/main.dart) contains a complete demonstration on how to use this plugin. -## For Mac & IOS developer +## For macOS & iOS developer I am new to Xcode and iOS developing, and I cannot find a better way to support both simulators and real devices without combining the binary frameworks. To reduce build size, change the `s.vendored_frameworks` in `ios/flutter_qjs.podspec` to the specific framework. @@ -146,4 +160,4 @@ Two additional notes: 1. quickjs built with `release` config has bug in resolving `Promise`. Please let me know if you know the solution. -2. `ios/make.sh` limit the build architectures to avoid combine conflicts. Change the `make.sh` to support another architectures. \ No newline at end of file +2. `ios/make.sh` limits the build architectures to avoid combining conflicts. Change the `make.sh` to support another architectures. \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index fc2018c..ba43bba 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -82,7 +82,7 @@ packages: path: ".." relative: true source: path - version: "0.2.0" + version: "0.2.1" flutter_test: dependency: "direct dev" description: flutter diff --git a/lib/flutter_qjs.dart b/lib/flutter_qjs.dart index 2d79227..7c30f5c 100644 --- a/lib/flutter_qjs.dart +++ b/lib/flutter_qjs.dart @@ -13,23 +13,28 @@ import 'package:ffi/ffi.dart'; import 'package:flutter_qjs/ffi.dart'; import 'package:flutter_qjs/wrapper.dart'; -/// Handle function to manage js call with `dart(method, ...args)` function. +/// Handler function to manage js call. typedef JsMethodHandler = dynamic Function(String method, List args); -/// Handle function to manage js module. +/// Handler function to manage js module. typedef JsModuleHandler = String Function(String name); class FlutterQjs { Pointer _rt; Pointer _ctx; + + /// Message Port for event loop. Close it to stop dispatching event loop. ReceivePort port = ReceivePort(); - /// Set a handler to manage js call with `channel(method, args)` function. + /// Handler function to manage js call with `channel(method, [...args])` function. JsMethodHandler methodHandler; - /// Set a handler to manage js module. + /// Handler function to manage js module. JsModuleHandler moduleHandler; + /// Quickjs engine for flutter. + /// + /// Pass handlers to implement js-dart interaction and resolving modules. FlutterQjs({this.methodHandler, this.moduleHandler}); _ensureEngine() { @@ -77,7 +82,7 @@ class FlutterQjs { _ctx = null; } - /// DispatchMessage + /// Dispatch JavaScript Event loop. Future dispatch() async { await for (var _ in port) { if (_rt == null) continue; diff --git a/lib/isolate.dart b/lib/isolate.dart index 7b4b8aa..9b69150 100644 --- a/lib/isolate.dart +++ b/lib/isolate.dart @@ -211,13 +211,17 @@ typedef JsIsolateSpawn = void Function(SendPort sendPort); class IsolateQjs { Future _sendPort; - /// Set a handler to manage js call with `channel(method, args)` function. - /// The function must be a top-level function or a static method + /// Handler to manage js call with `channel(method, [...args])` function. + /// The function must be a top-level function or a static method. JsMethodHandler methodHandler; - /// Set a handler to manage js module. + /// Asynchronously handler to manage js module. JsAsyncModuleHandler moduleHandler; + /// Quickjs engine runing on isolate thread. + /// + /// Pass handlers to implement js-dart interaction and resolving modules. The `methodHandler` is + /// used in isolate, so **the handler function must be a top-level function or a static method**. IsolateQjs({this.methodHandler, this.moduleHandler}); _ensureEngine() { @@ -257,6 +261,7 @@ class IsolateQjs { _sendPort = completer.future; } + /// Free Runtime and close isolate thread that can be recreate when evaluate again. close() { if (_sendPort == null) return; _sendPort.then((sendPort) { @@ -267,6 +272,7 @@ class IsolateQjs { _sendPort = null; } + /// Evaluate js script. Future evaluate(String command, {String name, int evalFlags}) async { _ensureEngine(); var evaluatePort = ReceivePort(); @@ -279,10 +285,9 @@ class IsolateQjs { 'port': evaluatePort.sendPort, }); var result = await evaluatePort.first; - if (result['error'] == null){ + if (result['error'] == null) { return _decodeData(result['data'], sendPort); - } - else + } else throw result['error']; } } diff --git a/pubspec.yaml b/pubspec.yaml index ec291fe..57152e2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_qjs description: This plugin is a simple js engine for flutter using the `quickjs` project. Plugin currently supports all the platforms except web! -version: 0.2.0 +version: 0.2.1 homepage: https://github.com/ekibun/flutter_qjs environment: