Published 2026-10-19
Source taken from js_interpreter:0.0.1.

A comprehensive, pure Dart implementation of a JavaScript interpreter supporting ES6+ features. Perfect for embedding JavaScript execution in Dart/Flutter applications.
import/exportfunction* and yield/yield* supportInt8Array, Uint8Array, Float32Array, etc.)For detailed feature coverage and use cases, check the test suite for comprehensive examples.
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
// Evaluate JavaScript code
final result = interpreter.eval('''
const greeting = "Hello";
const name = "World";
greeting + ", " + name + "!";
''');
print(result); // Hello, World!
}
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() async {
final interpreter = JSInterpreter();
final result = await interpreter.evalAsync('''
async function fetchData() {
const response = await Promise.resolve({ data: "Hello Async!" });
return response.data;
}
await fetchData();
''');
print(result); // Hello Async!
}
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() async {
final interpreter = JSInterpreter();
// Returns native Dart types (String, int, double, List, Map, etc.)
final result = await interpreter.evalAsyncToDart('''
const data = {
name: "Alice",
age: 30,
hobbies: ["reading", "coding"]
};
JSON.stringify(data);
''');
print(result); // {"name":"Alice","age":30,"hobbies":["reading","coding"]}
print(result.runtimeType); // String
}
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() async {
final interpreter = JSInterpreter();
// Register a module
interpreter.registerModule('math-utils', '''
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
export const PI = 3.14159;
''');
// Use the module
final result = await interpreter.evalModuleAsync('''
import { add, multiply, PI } from 'math-utils';
const sum = add(5, 3);
const product = multiply(4, PI);
export default { sum, product };
''');
print(result); // {sum: 8, product: 12.56636}
}
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
// Inject Dart function into JavaScript
interpreter.setGlobal('dartPrint', (List<JSValue> args) {
print('From Dart: ${args.first}');
return JSValueFactory.undefined();
});
interpreter.eval('''
dartPrint("Hello from JavaScript!");
''');
// Output: From Dart: Hello from JavaScript!
}
load(path) is available by default (similar to print() and trace()).
It reads the JavaScript file from disk and evaluates it in the current
interpreter context.
import 'dart:io';
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
final file = File('/tmp/helpers.js');
file.writeAsStringSync('''
function add(a, b) { return a + b; }
''');
interpreter.eval('load("/tmp/helpers.js");');
final result = interpreter.eval('add(2, 3);');
print(result); // 5
}
new Spreadsheet(...))When using pkg_spreadsheet, you can register a host-side constructor that is
callable from JavaScript with new.
Opt-in: this extension is not enabled by default.
You must callinterpreter.registerSpreadsheetClass()for eachJSInterpreterinstance that should exposeSpreadsheetandSheet.
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
interpreter.registerSpreadsheetClass();
interpreter.setGlobal('path', '/tmp/data.tsv');
final result = interpreter.eval('''
const spreadsheet = new Spreadsheet(path);
spreadsheet.read();
const firstSheet = spreadsheet.getFirstSheet(); // returns Sheet object
firstSheet.getCell(0, 0);
''');
print(result); // first cell as string
}
Registered globals and return types:
new Spreadsheet(path?)new Sheet(name, rows?)spreadsheet.getFirstSheet(), spreadsheet.getLastSheet(),
spreadsheet.getSheet(name), spreadsheet.sheet, spreadsheet.getSheets()
all return Sheet object(s)spreadsheet.getSheetNames() returns string[]You can run code in controlled mode, pause at configured source-line breakpoints, request a cooperative pause during execution, and resume from the paused state.
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
interpreter.setBreakpoints({3});
final first = interpreter.evalControlled('''
const a = 1;
const b = 2;
const c = a + b;
c;
''');
if (first.isPaused) {
print(first.pauseInfo?.reason); // ExecutionPauseReason.breakpoint
print(first.pauseInfo?.line); // 3
}
final resumed = interpreter.resumeExecution();
print(resumed.value); // 3
interpreter.setBreakpoints({4});
final second = interpreter.evalControlled('''
function add(a, b) {
return a + b;
}
const result = add(1, 2);
result;
''');
if (second.isPaused) {
final stepped = interpreter.stepExecution(); // step-over in current frame
print(stepped.pauseInfo?.reason); // ExecutionPauseReason.step
}
}
Host APIs:
evalControlled(code) starts a resumable execution sessionpauseExecution() requests a cooperative pause of the current controlled runresumeExecution() continues from the paused pointstepExecution() executes one step-over in the current framecanStepOver() reports whether step-over is currently validcanStepInto() reports whether step-into is currently valid (only when paused at a call-site whose runtime target is script-defined)canStepOut() reports whether stepping out is currently possible (caller frame exists)addPauseListener(...) receives all pause notificationsaddBreakpointListener(...) receives breakpoint-only pausesaddResumeListener(...) notifies when execution continues from a paused stateaddStepListener(...) / addStepOverListener(...) receive step pausesaddExecutionCompleteListener(...) / addExecutionErrorListener(...) notify terminal outcomesexecutionStatus / executionPauseInfo expose current control statePausing is cooperative, not preemptive: the VM pauses at instruction boundaries, not by forcefully interrupting Dart code mid-operation.
The interpreter provides a powerful bidirectional messaging system for complex Dart-JavaScript interactions.
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() {
final interpreter = JSInterpreter();
// Register a message handler
interpreter.onMessage('greet', (dynamic message) {
final name = message as String;
return 'Hello, $name!';
});
// Call from JavaScript (synchronous)
final result = interpreter.eval('''
const greeting = sendMessage('greet', 'Alice');
greeting; // "Hello, Alice!"
''');
print(result);
}
For async operations that require waiting for results:
import 'package:pkg_js_interpreter/pkg_js_interpreter.dart';
void main() async {
final interpreter = JSInterpreter();
// Register an async message handler
interpreter.onMessage('fetchUser', (dynamic message) async {
final userId = message as int;
// Simulate async operation
await Future.delayed(Duration(milliseconds: 100));
return {
'id': userId,
'name': 'Alice',
'email': 'alice@example.com'
};
});
// Call from JavaScript (asynchronous)
final result = await interpreter.evalAsync('''
async function loadUser() {
const user = await sendMessageAsync('fetchUser', 1);
return user.email;
}
await loadUser();
''');
print(result); // "alice@example.com"
}
Message Handlers with Multiple Arguments:
interpreter.onMessage('calculate', (dynamic message) {
final args = message as List;
final operation = args[0] as String;
final a = args[1] as num;
final b = args[2] as num;
switch (operation) {
case 'add': return a + b;
case 'multiply': return a * b;
default: return null;
}
});
// In JavaScript:
final result = interpreter.eval('''
sendMessage('calculate', 'add', 5, 3); // 8
sendMessage('calculate', 'multiply', 4, 7); // 28
''');
Async Pipeline with Promise.all:
interpreter.onMessage('fetchData', (dynamic message) async {
final source = message as String;
await Future.delayed(Duration(milliseconds: 50));
return {'source': source, 'data': [1, 2, 3]};
});
final result = await interpreter.evalAsync('''
const results = await Promise.all([
sendMessageAsync('fetchData', 'database'),
sendMessageAsync('fetchData', 'api'),
sendMessageAsync('fetchData', 'cache')
]);
results.map(r => r.source);
''');
// ['database', 'api', 'cache']
Error Handling:
interpreter.onMessage('riskyOperation', (dynamic message) {
if (message == 'fail') {
throw Exception('Operation failed');
}
return 'Success';
});
// In JavaScript:
final result = await interpreter.evalAsync('''
try {
const result1 = await sendMessageAsync('riskyOperation', 'ok');
const result2 = await sendMessageAsync('riskyOperation', 'fail');
} catch (error) {
console.log('Caught:', error.message);
}
''');
The main class for JavaScript interpretation.
| Method | Description |
|---|---|
eval(String code) |
Synchronously evaluate JavaScript code |
evalAsync(String code) |
Asynchronously evaluate JavaScript code (returns Future<JSValue>) |
evalAsyncToDart(String code) |
Asynchronously evaluate and convert result to Dart types |
evalModule(String code) |
Evaluate as ES6 module |
evalModuleAsync(String code) |
Asynchronously evaluate as ES6 module |
registerModule(String name, String code) |
Register a module for import |
setGlobal(String name, dynamic value) |
Set a global variable |
registerHostCommand(String name, HostCommand handler) |
Register one custom global JS command backed by Dart |
registerHostCommands(Map<String, HostCommand> commands) |
Register multiple custom global JS commands |
getGlobal(String name) |
Get a global variable |
onMessage(String channel, dynamic callback) |
Register a message handler for a specific channel |
setConsoleCallbacks({onLog, onWarn, onError}) |
Capture console.log/warn/error output in Dart callbacks (suppresses default printing for registered levels) |
final interpreter = JSInterpreter();
final logs = <String>[];
final warns = <String>[];
final errors = <String>[];
interpreter.setConsoleCallbacks(
onLog: logs.add,
onWarn: warns.add,
onError: errors.add,
);
interpreter.eval('''
print("uses same log callback channel as console.log");
console.log("hello", 123);
console.warn("careful");
console.error("boom");
''');
| Function | Description |
|---|---|
print(...args) |
Built-in alias of console logging; auto-registered on every JSInterpreter |
sendMessage(String channel, [arg1, arg2, ...]) |
Send a synchronous message to Dart and get a result |
sendMessageAsync(String channel, [arg1, arg2, ...]) |
Send an asynchronous message to Dart (returns Promise) |
| Type | Description |
|---|---|
JSUndefined |
JavaScript undefined |
JSNull |
JavaScript null |
JSBoolean |
JavaScript boolean |
JSNumber |
JavaScript number (int or double) |
JSString |
JavaScript string |
JSBigInt |
JavaScript BigInt |
JSSymbol |
JavaScript Symbol |
JSObject |
JavaScript object |
JSArray |
JavaScript array |
JSFunction |
JavaScript function |
JSClass |
JavaScript class |
JSGenerator |
JavaScript generator |
JSAsyncGenerator |
JavaScript async generator |
JSPromise |
JavaScript Promise |
JSRegExp |
JavaScript RegExp |
JSDate |
JavaScript Date |
JSError |
JavaScript Error (and all error types) |
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ in Dart
JSInterpreter:
stepOver()stepInto()stepOut()JSInterpreter:
canStepOver()canStepInto()canStepOut()stepExecution() did alias stepOver()JSInterpreter for controlled evaluation, cooperative pause, and resume:
evalControlled(...)pauseExecution()resumeExecution()stepExecution()stepOverExecution()executionStatusexecutionPauseInfoJSInterpreter:
addPauseListener(...), addBreakpointListener(...), addResumeListener(...)addStepListener(...), addStepOverListener(...)addExecutionCompleteListener(...), addExecutionErrorListener(...)js_interpreter:0.0.1.