export function to global scope in scalajs - javascript

Question about scalajs and javascript.
How to mark a function to be purely exported to global scope?
Given scala object in top level package
#JSExport
object Foo{
def apply(a: Int, b: Int): String = "bar"+a+b
}
I would like to compile this scala code into javascript and have this function named Foo in global scope. In other words I would like to have a javascript similar to this:
function Foo(a,b) {
return 'bar'+a+b;
}
Is it possible using scalajs?
I am writing component in javascript, which will be referenced from third party API which can not be influenced by me. This is why I simply need to follow their rules and provide javascript functions in global scope.

The solution to this is now Top Level Exports, as documented under the now-closed issue #1381 - to use this feature, tag a function in an object with #JSExportTopLevel.
object A {
#JSExportTopLevel("foo")
def foo(x: Int): Int = x + 1
}
<Foo will be available from JavaScript's global namespace>
(See https://www.scala-js.org/doc/interoperability/export-to-javascript.html for the official documentation, under "Exporting top-level methods".)

You currently cannot do this without executing some code. But you can have setup code that assigns it:
import scala.scalajs.js
object App extends js.JSApp {
def main(): Unit = {
js.Dynamic.global.anything = // your js.FunctionN
}
}
There is an issue open (#1381) to have language support for this.

Related

PyMiniRacer adding Python classes to JS scope

Using PyMiniRacer, I would be able to use Python objects in Javascript; using the PyV8 library:
import PyV8
class Scope(PyV8.JsClass):
def __init__(self):
self.obj1 = Object1()
self.obj2 = Object2()
pythonScope = Scope()
context = PyV8.JsContext(pythonScope)
context.enter()
context.eval(...)
And using this code, the javascript can access the properties of Scope: obj1 and obj2
For PyMiniRacer, looking at the code, the MiniRacer class doesn't appear to accept any arguments into the constructor, so I don't see how I could add a Python Scope class into the JS scope. Is there a specific way of defining the class to be able to add a set of python classes to the JS scope, or do I need to inject them into the JS scope using a method I have missed while looking over the source code?
In the Ruby RubyRacer, (I understand that the RubyRacer and PyMiniRacer are individual projects by different authors, although PyMiniRacer is inspired by RubyRacer) objects in the Ruby scope can be embedded by calling context["funcName"] = funcName], but in Python...
>>> from py_mini_racer import py_mini_racer
>>> context = py_mini_racer.MiniRacer()
>>> def helloWorld(num):
return num * 2
>>> context["helloWorld"] = helloWorld
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
context["helloWorld"] = helloWorld
TypeError: 'MiniRacer' object does not support item assignment
>>>
...it raises an error. I also tried context.__dict__["helloWorld"] = "helloWorld", and running context.eval(helloWorld(5)) returns a ReferenceError: helloWorld is not defined error. All it does is allow me to call context.helloWorld(5) which doesn't help in executing from JS.
How can I insert Python objects into the JS scope such that in the JS code, I can call and access the methods and attributes from the Python object?
Unfortunately, PyMiniRacer does not support attaching Python objects or functions to a JavaScript Context so it is not possible to call Python code from the JavaScript code.

Using 'export' keyword solely for importing into Unit Tests

I'm using Meteor and am writing unit tests for a Collection. I've got Helper methods for the collection in addition to just regular JS functions.
I.e.
Collection.helpers({
helperFn: function () {
return 'foo';
}
});
//And in the same file
function bar() {
return "bar";
}
Then in my tests file I have something like
import { Collection } from '../collections'
//Use Factory or Stub to create test Document
//This then works just fine and I can assert, etc..
testDoc.helperFn
My question is with wanting to test just the regular 'bar' JS function. This isn't a big deal using ES6 classes because then I can just export the whole class and call any function with an instance of it. But with Meteor I'm finding the only way I can access the function is by using the 'export' keyword.
So in my Collection file
export function bar ({ return bar; });
And now in my test file I'd do something like
import { bar } from '../collection'
I'd rather not add an export statement for every time I test a new function. Is there any way around this or is it not a big deal?
I do think that the export/import is the way to go, but to answer the first part of your question: yes, you can fall back to the original scoping of meteor and put these functions in the global scope of meteor as follows:
do not put your files in the imports/ folder, but into another folder in your project, e.g., server/.
defined the functions as:
bar = function() { /* function body */ }
These variables are interpreted by meteor as being global to the project, and hence do not need to be imported before use.
That said, there was a reason meteor introduced the imports/ folder and corresponding export/import paradigm in version 1.3. It avoids polluting the global scope and makes it much easier to see where things are defined.

How to define a global js function in Kotlin?

Every function and variable that I create in KotlinJs project gets into a module. But I need to define some functions in global scope.
I use p5js library (pure js). It allows user to define event handling functions in global scope. I'm trying to use KotlinJS in this project. But I don't know how to create global functions to handle p5js's events. All my Kotlin functions are inside of the module. And to call my Kotlin code I need to specif the full name mymodule.draw()
Currently I have to make an additional layer of pure JS code with global funcs that translate execution to kotlin functions which looks like this:
function setup() {
mymodule.setup();
}
function draw() {
mymodule.draw();
}
The problem with this approach is a lot of boilerplate and repetitive code.
In case this will be useful for somebody I will leave another workaround here:
import kotlin.browser.window
fun main() {
window.asDynamic()["setup"] = ::setup
window.asDynamic()["draw"] = ::draw
}
fun setup() {}
fun draw() {}
What it actually does, it creates functions in kotlin module as usual and then assigns them to window object, which makes it global.
This solution is still not ideal, cause it needs a manual assignment for every function. At least it does it right in Kotlin project, no need to maintain a separate pure js file.
Maybe it is possible to create an annotation and leverage kotlin reflection(no idea how it's supported in KotlinJS).
Although this solution works for me, I would like to have some out of the box solution like they do for #JsNonModule external functions.
Adding on top of #Sergey's Answer, one can also use this work around when dealing with libs like p5.js
fun main() {
window.asDynamic().setup = {
// your setup code here
}
window.asDynamic().draw = {
// your draw code here
}
}
This approach minimizes the definition and the declaration of the two functions (looking at you C Language). Thanks
Unfortunately there is no way to define global function in Kotlin/JS. It is possible to use Plain module type where you have global symbols in module object which is defined in global scope.
// module M
fun foo() {}
which is accessible via M.foo

Define 'real' private methods in ES6 Module/Class in a nodejs only environment without any information leak

I know that there is no REAL private method INSIDE ES6 classes. However I was playing around a little bit and discovered something good - maybe...
As I mentioned it is not possible to not expose properties of an object. But I've tried to achieve somewhat of OOP programming as I divided my classes into seperate files and then exported those classes like:
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
/**
* Do public stuff...
*/
}
}
// expose class to environment.
export default MyClass;
So I can import the class:
import MyClass from './MyClass.js';
Of course myMethod is accessible from any other file which imported the module. Sinced I was in need of variables and functions beeing accessible only by the class I've tried this:
// private variable outside of class scope but still accessible.
let possiblePrivateVariable = 'am I a private variable?';
class MyClass {
constructor() {
/**
* Initialize stuff...
*/
}
myMethod() {
// run private method.
console.log(_possiblePrivateMethod());
// show private variable.
console.log(possiblePrivateVariable);
}
}
// private function outside of class scope but still accessible.
function _possiblePrivateMethod() {
return 'am I a private method?';
}
// expose class to environment.
export default MyClass;
Now you can't access the private variable and private method:
// Import class to newFile.js and use it.
import MyClass from './MyClass.js';
const exposedClass = new MyClass();
exposedClass.possiblePrivateVariable; // -> undefined.
exposedClass._possiblePrivateMethod(); // -> undefined.
exposedClass. myMethod(); // -> logs: am I a private method?
// am I a private variable?
It is obvious that those are feeling like beeing 'private' because I am not exposing them with the word export. My question is if this method can be considered creating private variables and methods? And if the Method I've shown has any other possible leakage while running the code?
Regards,
Megajin
Sidenote: I do not need browser support since it is a NodeJS only environment. I am using NodeJS v8.1.4 and use babel in my npm start script so I can use import without any TypeError's.
I should mention as well that I'm aware that this question could be seen as a duplicate but it is not because this question is not about private properties, variables and methods inside a class but outside it.
My question is if this method can be considered creating private
variables and methods?
Yes it's an actual working solution to address the fact that ES6/7/8 do not handle privacy in classes.
And if the Method I've shown has any other possible leakage while
running the code?
Fast answer : No leak
Detailed answer:
In your private function system, what makes the function private is that in javascript the scope of a function is defined by where it's defined. In your case the file.
If you do not export the function outside the file, there is no way to access it. Like you cannot access the function in the following mainstream example :
function toto() {
const tmpToto = () => console.log('Hello');
// I can access tmpToto from here
tmpToto();
return false;
}
// I cannot access tmpToto from here
Here you get a nice explanation about scopes
More infos according to comment
How would you solve the problem that multiple class instances will
share the 'out of scope' variables?
You can use IIFE(Immediately-invoked function expression) as described by #Son JoungHo in this post.
let Name = (function() {
const _privateHello = function() {}
class Name {
constructor() {}
publicMethod() {
_privateHello();
}
}
return Name;
})();

Integration clojurescript into a javascript framework

I would like to use Clojurescript to write a component within a Javascript framework but I can't work out how to create the constructor and call global variables within the object.
The framework creates views (within their own .js file) by reading their state from a saved jason file and reifying them in javascript (the views are code like so):
(function() {
var Title = function(json) {
view.View.call(this, json); // view is defined in another js file - global namespace
this.title = json.title;
this.el.addClass("title");
}
view.inherit(view.View, Title);
view.Title = Title;
view.types.Title = Title;
Title.prototype.json = function() {
return $.extend(view.View.prototype.json.call(this), {
type: 'Title',
title: this.title
});
}
Title.prototype.reflow = function() {
this.h2.quickfit(opts);
}
})();
I have seen how you create a Javascript object using deftype macro and Object:
(deftype Foo [a b c]
Object
(bar x (+ a b c x)))
I'm new to both javascript and clojurescript. I see that the anonymous function wrapping everything provides a scope for the view but not sure how to (or if I need to) so something equivalent in clojurescript.
So my questions are:
how do I create the constructor function for Title in this model??
And do how should I handle the calls to the view variable, such as view.inherit etc?
Thanks
This is a bit general of an answer, but you seem to be wanting to share code between both ClojureScript and JavaScript, so here is a primer and a few select tidbits on Using JavaScript Libraries in Clojure:
Exporting
Protecting symbols you declare from renaming is easy; just define :export metadata on any ClojureScript var, and the ClojureScript compiler will ensure that it is not munged.
For example, a function can be declared like this:
(ns example)
(defn ^:export hello [name]
(js/alert (str "Hello," name "!")))
It is then available, via the same name, in an external JavaScript context:
<script>
example.hello("Eustace")
</script>
Externs
In order to go the other way, and reference a variable declared externally from within your code, you must provide the Google Closure compiler with an "extern file", a .js file defining javascript variables which will not be munged. This file is passed to the Google Closure compiler, and when compiling, it will not munge any names defined in it.
For example, I can try compiling the following ClojureScript (utilizing the Raphael.js library) in advanced mode without an extern file.
(defn test []
(let [raphael (js/Raphael. 10 50 320 200)]
(. raphael (circle 50 50 50))))
When I call the test function, it will throw a javascript error that new Raphael(10, 50, 320, 200)).K is not a function. K is the munged name of the circle function from Raphael, and obviously can't be found, since it isn't defined anywhere. We need to tell the compiler to preserve the name circle, not munge it.
I can do this by creating the following externs.js file:
var Raphael = {};
Raphael.circle = function() {};
And use it when compiling my ClojureScript:
(closure/build "src/cljs" {:optimizations :advanced
:externs ["externs.js"]
:output-to "helloworld.js"})

Categories

Resources