How to access Javascript module with Duktape in Android - javascript

I am successfully parsing and evaluating a javascript file with Duktape in my Android application using Kotlin.
val file_name = "lib.js"
val js_string = application.assets.open(file_name).bufferedReader().use {
it.readText()
}
val duktape = Duktape.create()
try {
Log.d("Greeting", duktape.evaluate("'hello world'.toUpperCase();").toString())
duktape.evaluate(js_string)
} finally {
duktape.close()
}
The javascript file was created with Browserify, so it is one single file with everything and it is working fine. But I need to request a module and a method from the module, example:
var test = require('testjs-lib');
test.EVPair.makeRandom().toWTF();
I have no idea of how to do it and have not found any example, besides this link: http://wiki.duktape.org/HowtoModules.html
It tells me to use a modsearch, but I don't have a clue how to do it or where it should be placed, not even if it is applicable for the Duktape Android (https://github.com/square/duktape-android).
Has anybody done it successfully that could shed some light on this matter?

in the testjs-lib.js, add the JS code that makes use of the module testjs-lib.js itself exports. For example:
function myModule() {
this.hello = function() {
return 'hello!';
}
this.goodbye = function() {
return 'goodbye!';
}
}
module.exports = myModule;
//You code goes here
console.log(myModule.hello());
console.log(myModule.goodbye());
Then ask Duktape to evaluate the entire file.

Say you want to include Underscore in duktape.
Put your module/library code in a separate js file. In an android project, you can put this js file in Assets folder. In our example, it'd be sth like: underscore.js
Create a java interface that'd be used by duktape to get inputstream to this js file.
Sth like:
```
public interface DuktapeHelper {
#JavascriptInterface
String getUnderScore();
}
````
Bind this java interface to a js interface in your duktape instance.
```
duktape.bind("helper", DuktapeHelper.class, <instance of your DuktapeHelperImplementation>);
```
Implment modSearch function in duktape using helper interface that you injected before.
```
duktape.evaluate("Duktape.modSearch = function (id) {\n" +
" if (id == \"underscore\") {" +
" return helper.getUnderScore();" +
" } " +
" throw new Error('cannot find module: ' + id);" +
" };" +
"var _ = require('underscore')._; ");
```

Related

Ifc.js: wasm streaming compile failed: LinkError: import object field 'a' is not a Memory

I am reading IFC files and ifc.js seemed like a solid option although I am not so experienced with javascript but I thought it could be a good opportunity to learn about it.
I followed the documentation example that can be food here ``https://ifcjs.github.io/info/docs/Hello%20world```.
I packed the app inside of a django project and everything is fine until I try to load up a file.
I am getting the following error:
RuntimeError: abort(LinkError: import object field 'a' is not a Memory). Build with -s ASSERTIONS=1 for more info.
On my browser debugger, the error links to the following class of my bundle.js file
class IFCLoader extends Loader {
constructor(manager) {
super(manager);
this.ifcManager = new IFCManager();
}
load(url, onLoad, onProgress, onError) {
const scope = this;
const loader = new FileLoader(scope.manager);
this.onProgress = onProgress;
loader.setPath(scope.path);
loader.setResponseType('arraybuffer');
loader.setRequestHeader(scope.requestHeader);
loader.setWithCredentials(scope.withCredentials);
loader.load(url, async function (buffer) {
try {
if (typeof buffer == 'string') {
throw new Error('IFC files must be given as a buffer!');
}
onLoad(await scope.parse(buffer));
} catch (e) {
if (onError) {
onError(e);
} else {
console.error(e);
}
I have no clue how to correct this issue and any help would be highly appreciated. I am happy to post additional files or code if needed. Thanks

How to test JavaScript without a framework

How can I test JavaScript code without using an additional framework such as Mocha? Is it possible to create a unit test case, write test functions manually, test the code, etc.?
I've tried to write a test case, but even though they were in the same folder, I couldn't link them.
Let's say this is a function in the main.js file
function calculate(a, b) {
return a + b;
}
And this is a test case in the testMain.js file
function testCalculate(){
if(calculate(1, 1) == 2)
console.log('It Works!');
else
console.log('Test failed');
}
testCalculate();
When I try to run testMain.js in the IntelliJ IDEA IDE I get an error similar to
"ReferenceError: calculate is not defined"
It depends whether you are trying to test Node.js code or front end code. In both cases you have to "expose" the function under test to your test framework.
Node.js
// main.js
const obj = {};
obj.sum = (a, b) => {
return a+b;
};
module.exports = obj; // Export 'obj' so that it is visible from your test runner
// test.js
const main = require('main.js');
const assert = require('assert');
const it = (desc, fn) => {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', `\u2714 ${desc}`);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', `\u2718 ${desc}`);
console.error(error);
}
};
it('should return the sum of two numbers', () => {
assert.strictEqual(main.sum(5, 10), 15);
});
When you run node test.js you should be able to see the test result.
Front End
// app.js
self.myapp = myapp; // All the methods in myapp will be exposed globally
myapp.sum = function(a, b) {
return a + b;
}
// test.js
function it(desc, fn) {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', '\u2714 ' + desc);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', '\u2718 ' + desc);
console.error(error);
}
}
function assert(condition) {
if (!condition) {
throw new Error();
}
}
it('should return a sum of two integers', function(){
assert(myapp.sum(5, 10) === 15);
});
// test.html - This is your test runner for the front end
<html>
...
<body>
...
<script src="app.js"></script>
<script src="test.js"></script>
</body>
</html>
Open test.html in a browser and open the browser console. You should be able to see the success message.
This way you can write test cases for Node.js and front end JavaScript code without using Mocha or any other framework.
To make your code work, your testMain.js file needs to import your main.js code somehow.
In the main.js file:
function calculate(a, b) {
return a+b;
}
module.exports.calculate = calculate
in testMain.js file, import the main.js:
var main = require('main.js')
function testCalculate(){
if(main.calculate(1+1)==2)
console.log('It Works!');
else
console.log('Test failed');
}
Note: I'm aware this isn't necessarily showing good coding style, just aiming to demonstrate what the original issue was with minimal changes to the original snippets
That said, it's not usually worth reinventing the wheel and building your own test framework. Can you clarify the reason why you would like to avoid an existing framework? If you are looking for simplicity, maybe something like jstinytest would do the trick.
If it is a Node.js application you can simply require the other file and import the other function. If the project uses Babel you can use ES6 import to import the function from the other file.
I was also bothered by the same issue for a while. The question is how to test your JavaScript code with out a testing framework since testing frame works bring a lot to the table and most of the time they get into the way.
The answer is to use assertion libraries without the testing frame work. For example you can just use chai assertion library with out mocha frame work
you can simple install chai with
npm install chai
After that you can just use it:
var should = require('chai').should()
const log = console.log;
//log(should);
//const letters = "abcdef";
const letters = 555;
letters.should.be.a('string');

How do I write a LZ compressed string to text file using JXA?

I am trying to write a JXA script in Apple Script Editor, that compresses a string using the LZ algorithm and writes it to a text (JSON) file:
var story = "Once upon a time in Silicon Valley..."
var storyC = LZString.compress(story)
var data_to_write = "{\x22test\x22\x20:\x20\x22"+storyC+"\x22}"
app.displayAlert(data_to_write)
var desktopString = app.pathTo("desktop").toString()
var file = `${desktopString}/test.json`
writeTextToFile(data_to_write, file, true)
Everything works, except that the LZ compressed string is just transformed to a set of "?" by the time it reaches the output file, test.json.
It should look like:
{"test" : "㲃냆੠Њޱᐈ攀렒삶퓲ٔ쀛䳂䨀푖㢈Ӱນꀀ"}
Instead it looks like:
{"test" : "????????????????????"}
I have a feeling the conversion is happening in the app.write command used by the writeTextToFile() function (which I pulled from an example in Apple's Mac Automation Scripting Guide):
var app = Application.currentApplication()
app.includeStandardAdditions = true
function writeTextToFile(text, file, overwriteExistingContent) {
try {
// Convert the file to a string
var fileString = file.toString()
// Open the file for writing
var openedFile = app.openForAccess(Path(fileString), { writePermission: true })
// Clear the file if content should be overwritten
if (overwriteExistingContent) {
app.setEof(openedFile, { to: 0 })
}
// Write the new content to the file
app.write(text, { to: openedFile, startingAt: app.getEof(openedFile) })
// Close the file
app.closeAccess(openedFile)
// Return a boolean indicating that writing was successful
return true
}
catch(error) {
try {
// Close the file
app.closeAccess(file)
}
catch(error) {
// Report the error is closing failed
console.log(`Couldn't close file: ${error}`)
}
// Return a boolean indicating that writing was successful
return false
}
}
Is there a substitute command for app.write that maintains the LZ compressed string / a better way to accomplish what I am trying to do?
In addition, I am using the readFile() function (also from the Scripting Guide) to load the LZ string back into the script:
function readFile(file) {
// Convert the file to a string
var fileString = file.toString()
// Read the file and return its contents
return app.read(Path(fileString))
}
But rather than returning:
{"test" : "㲃냆੠Њޱᐈ攀렒삶퓲ٔ쀛䳂䨀푖㢈Ӱນꀀ"}
It is returning:
"{\"test\" : \"㲃냆੠Њޱᐈ攀렒삶퓲ٔ쀛䳂䨀푖㢈Ӱນꀀ\"}"
Does anybody know a fix for this too?
I know that it is possible to use Cocoa in JXA scripts, so maybe the solution lies therein?
I am just getting to grips with JavaScript so I'll admit trying to grasp Objective-C or Swift is way beyond me right now.
I look forward to any solutions and/or pointers that you might be able to provide me. Thanks in advance!
After some further Googl'ing, I came across these two posts:
How can I write UTF-8 files using JavaScript for Mac Automation?
read file as class utf8
I have thus altered my script accordingly.
writeTextToFile() now looks like:
function writeTextToFile(text, file) {
// source: https://stackoverflow.com/a/44293869/11616368
var nsStr = $.NSString.alloc.initWithUTF8String(text)
var nsPath = $(file).stringByStandardizingPath
var successBool = nsStr.writeToFileAtomicallyEncodingError(nsPath, false, $.NSUTF8StringEncoding, null)
if (!successBool) {
throw new Error("function writeFile ERROR:\nWrite to File FAILED for:\n" + file)
}
return successBool
};
While readFile() looks like:
ObjC.import('Foundation')
const readFile = function (path, encoding) {
// source: https://github.com/JXA-Cookbook/JXA-Cookbook/issues/25#issuecomment-271204038
pathString = path.toString()
!encoding && (encoding = $.NSUTF8StringEncoding)
const fm = $.NSFileManager.defaultManager
const data = fm.contentsAtPath(pathString)
const str = $.NSString.alloc.initWithDataEncoding(data, encoding)
return ObjC.unwrap(str)
};
Both use Objective-C to overcome app.write and app.read's inability to handle UTF-8.

How to use a static javascript function from another javascript file?

Hello and thank you for your time.
I am trying to make a JavaScript example application working.
The fact is I do not know exactly how to use this static function:
core.utils.js
static parseUrl(url) {
const data = {};
data.filename = '';
data.extension = '';
data.pathname = '';
data.query = '';
let parsedUrl = URL.parse(url);
data.pathname = parsedUrl.pathname;
data.query = parsedUrl.query;
if (data.query) {
// Find "filename" parameter value, if present
data.filename = data.query.split('&').reduce((acc, fieldval) => {
let fvPair = fieldval.split('=');
if (fvPair.length > 0 && fvPair[0] == 'filename') {
acc = fvPair[1];
}
return acc;
});
}
// get file name
if (!data.filename) {
data.filename = data.pathname.split('/').pop();
}
// find extension
let splittedName = data.filename.split('.');
if (splittedName.length <= 1) {
data.extension = 'dicom';
} else {
data.extension = data.filename.split('.').pop();
}
if (!isNaN(data.extension)) {
data.extension = 'dicom';
}
if (data.query &&
data.query.includes('contentType=application%2Fdicom')) {
data.extension = 'dicom';
}
return data;
}
Into the javascript file which has the appliaciton's logic.
I have tried the direct import used by the IDE itself, webstorm in my case:
import CoreUtils from "../../src/core/core.utils";
However, the browser's console says:
Uncaught SyntaxError: Unexpected identifier
And the line where is the error is:
import CoreUtils from "../../src/core/core.utils";
Also I have tried by myself to fix this, and I have read:
Calling a javascript function in another js file
So then I did what Fernando Mendez explains, which is to import the js file in the index.html, as:
<script src="../../src/core/core.utils.js"></script>
So then I commented out the import in the application js:
// import CoreUtils from "../../src/core/core.utils";
And the result is:
Uncaught SyntaxError: Unexpected identifier
In the following line:
import Validators from './core.validators';
Would you be kind and help me a little bit?
In addition my question is related to a previous one:
Javascript Trying to run AMIjs examples in local it does not work
However I do put this separatedly because of I think the problem is with JavaScript and not a Library.
Thank you for reading me.
Are you trying to call the static js function from another js file?
It looks like you have a few problems going on here.
1.) import CoreUtils from "../../src/core/core.utils";
From your example code of CoreUtils, I don't see a exported CoreUtils object from src/core/core_utils.js.
In order to utilize the import statement to reference code in another file, you need to export the object from the original file. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
This can be accomplished quickly by modifying your function definition in core_utils.js as follows:
change static parseUrl(url) { to export function parseUrl(url) {
Then, in application.js:
change import CoreUtils from "../../src/core/core.utils"; to import {parseUrl} from "../../src/core/core.utils"
It looks like you are running into a similar error with import Validators from './core.validators';
Hope that helps!
Try this:
Your parseUrl(url) function should be public, so change static parseUrl(url) to export function parseUrl(url).
Inside your main javascript file, import it like so:
import { parseUrl } from "../../src/core/core.utils";
And then just use it: var parsed = parseUrl(url);
Also, check this out

require() returns an empty object

Hi.
I've been on this for a while now, and cannot figure out why my dynamic module loader does not load files correctly. It simply just returns an empty object for every file.
function loadCommands() {
require('fs').readdirSync(require('path').join(__dirname, 'commands')).forEach(file => {
let commandName = file.substring(0, file.length - 3); // this removes the .js extension off of the file.
commands[commandName] = require('./commands/' + file);
});
}
This code should load each file in the ./commands directory and put it in an object called commands.
The require() function returns an empty object.
A sample command file contains the following:
const Command = require('../Command');
class Ban extends Command {
constructor(options) {
this.description = 'Bans a user.';
}
execute() {
}
}
module.exports = Ban;
Command contains the following:
class Command {
constructor() {
this.description = 'unknown';
}
execute() {
}
}
module.exports = Command;
If I do commands["ban"], it returns an empty object instead of the class I was expecting.
I am really confused onto what is going on here, as there's no circular dependencies at all.
If anyone can help me, it would be appreciated.
Thanks.

Categories