21. JavaScript
Overview
Fantom provides support for compiling to JavaScript and running in JavaScript VMs such as web browsers. Most of the sys API is available, however not all pods and APIs are accessible due to limitations of the JavaScript VM environment.
Js Facet
You must explicitly mark types you intend to compile to JavaScript using the Js
facet:
@Js class GonnaBeJs { Void sayHi() { Win.cur.alert("Hello!") } }
Deployment
The JavaScript compiler works by translating Fantom source code directly to JavaScript source code at compile time. This differs from the JVM/CLR, which work by using an intermediate format and translating to the target platform at runtime.
All Fantom source code in a pod marked as @Js
will be compiled to a single JavaScript source file. This file will also include reflection information and other meta-data needed at runtime. The file is named <podname>.js
and is packaged into the root directory of the pod file.
As JavaScript files are interpreted in the order they are parsed, each pod JS file must be written in the correct order. To simplify this process, the FilePack
API provides conveniences to generate a file list that guarantee the correct dependency order:
// expands to: sys.js, concurrent.js, graphics.js, web.js, dom.js files := FilePack.toAppJsFiles([Pod.find("dom")])
Browser Runtime
Web browsers are the primary target for Fantom JS, so most of the APIs are focused on simplifying how to wire up Fantom-based web apps.
Using FilePack
is the easiest way to bundle up your dependencies and serve up to the browser:
files := FilePack.toAppJsFiles(pods) pack := FilePack(files) ... override Void onGet() { switch (req.modRel.path.first) { case "myApp.js": pack.onGet ... } } ... out.head .initJs(["main":"myApp::Main"]) .includeJs(`/myApp.js`) .headEnd
A complete example can be found in js-hello.
Environment Initialization
The Fantom JS runtime can be initialized with custom configuration using the Env.vars
API. No explicit initialization is required by default. Simply parsing the source code will produce a valid runtime available at Win.onLoad
:
out.head.includeJs(`/myApp.js`).headEnd
To customize the default behavior, use WebOutStream.initJs
to initialize the desired Env.vars
. This must occur before any pod JS is parsed:
out.head .initJs(["main":"myApp::Main", "timezone":"Denver"]) .includeJs(`/myApp.js`) .headEnd
Principally this method is used to specify the "main" method to bootstrap your application at load time. See WebOutStream.initJs
for full list supported Env.vars
.
See js-env for example code for setting up timezones and locales.
Alternative Runtimes
You can run JavaScript compiled from Fantom by loading the pod's JavaScript source file into any JavaScript VM. There are no special requirements. Most of the information from the above sections should apply to other JsVMs.
Invoking Fantom from JavaScript
Fantom types are normal JavaScript objects, so they can be invoked natively from JavaScript code. Types are are formatted as:
fan.<myPod>.<myType>.<method> fan.myPod.MyType.staticMain(); // invoke a static method fan.myPod.MyType.make().instanceMain(); // invoke method from instance
An example using an HTML event handler:
<button onclick="fan.myPod.MyType.doSomething();">Click Me</button>
Fields are compiled into JavaScript as follows:
- the field itself is prefixed with "m_"
- getter matches field name
- the setter is suffixed with the "$" character
- const fields do not have a getter (must access as m_myField)
Field examples:
instance.myField() // call getter instance.myField$(value) // call setter instance.m_field // access field storage
Any Fantom slot that conflicts with a JavaScript keyword will be prefixed with "$". For example a slot named "var" will be accessed in JavaScript as "$var".
Refer to the JavaScript source code in sys and dom to see how the various types are implemented.
Natives
To compile JavaScript natives, add the source directories to your build script using the jsDirs
field. See Build Pod for an example.
The JavaScript code must follow the compiler conventions discussed above.
Testing
Fantom includes built-in support for fant
to run units test in a JavaScript VM using the -js
flag:
$ fant -js myPod
To run JS tests you need to have NodeJs installed. For macOS you can install using Homebrew:
$ brew install node