Introduction

EZMAScript is a statically typed, multi-purpose scripting language, primarily used for developing native applications through the ZoneGFX platform.

EZMAScript uses the ES file extension.

(EZMA stands for "Elegant-Zone Modular Applicable")

Equivalence

EZMAScript is equivalent to the TypeScript language and implemented using the TypeScript Compiler API, although with different coding conventions.

Hello World

The following prints a hello-world message to the console:

trace("Hello, world!");

Modules

Module structure usually looks as follows:

<project>
├── src/
│   ├── equations/
│   │   ├── __mod__.es
│   │   └── RungeKutta.es
│   ├── sat/
│   │   ├── __mod__.es
│   │   └── FineSAT.es
│   └── __mod__.es
└── zonegfx.toml

Given the project is a package identified as org.generalrelativity.foam and the source path is the default (src/), this results in three modules:

  • org.generalrelativity.foam - This would be src/__mod__.es
  • org.generalrelativity.foam.equations
  • org.generalrelativity.foam.sat

And two item-like modules:

  • org.generalrelativity.foam.equations::RungeKutta
  • org.generalrelativity.foam.sat::FineSAT

Further explanation:

  • The __mod__.es file is the entry point of a module.
  • The RungeKutta.es and FineSAT.es files are item-like modules.

A module source looks as follows:

// src/sat/__mod__.es
export * from "./FineSAT";

// src/sat/FineSAT.es
export class FineSAT {
    //
}

Imports

Here are some snippets on how import declarations look for importing items from a package:

// <source-path>/equations/__mod__.es
import * as equations from "org.generalrelativity.foam.equations";

// <source-path>/sat/__mod__.es
import { FineSAT } from "org.generalrelativity.foam.sat";

Internal imports

A package may import an item of itself in different ways. The most basic is using relative imports:

import { A } from "./A";
import { B } from "../B";

However, for convenience or for avoiding the ../../.. hell, you can use the actual package ID, and, if needing to resolve to an item-like module, use a trailing :: followed by the item name.

import { Account } from "com.ea.n4s.account::Account";

For example, this could be equivalent to importing from src/account/Account.es.

ESDoc

Tags

@default

Specifies a plain EZMAScript expression assumed as the default value of an item.

@default val

@deprecated

@deprecated Optional message

@event

Indicates that a property is an event handler.

@event

Example usage:

type ButtonProperties = {
    /**
     * @event
     */
    click?: (e: MouseEvent) => void,
};

@example

Attaches an example. If an @example tag contains no code blocks, then it is entirely treated as code.

@example
trace("hi");

@example
Example description.
```
trace("hi")
```

@hidden

Equivalent to @private.

@hidden

@package

Indicates that the ESDoc comment belongs to the file rather than to the item following it.

@package

@param

@param paramName Description

@private

Equivalent to @hidden.

@private

@return

@return Description

@see

@see [Display text](https://google.com)
@see {@link itemReference}

@throws

@throws {RangeError} Optional description

References

Include a link to a EZMAScript declaration anywhere by using {@link ...}.

{@link a.B}
{@link a.B | Display text}

Events

Class-based EventRecord

Here is a code snippet demonstrating how to structure a simple class with a compile-time event record.

/**
 * Example signalizer.
 */
class Example extends EventTarget {
    //
    declare [EventRecord]: {
        /**
         * Example event.
         */
        example: Event,
    };
}

Class trees should explicitly inherit the supertype's event record through intersection types A & B:

class A extends EventTarget {
    declare [EventRecord]: {
        ready: Event,
    };
}

class B extends A {
    declare [EventRecord]: A[typeof EventRecord] & {
        updated: CustomEvent<number>,
    };
}

class C extends B {
    declare [EventRecord]: B[typeof EventRecord] & {
        beep: CustomEvent<boolean>,
    };
}

Embed

The global Embed() function is a compiler-processed function that is used for embedding files into the program. It may only resolve to files under either the main source path or the target artifact directory.

  • The following results into an external app: URL or data: URL for short files:
Embed("thumb.webp")
  • The following results into a string from an UTF-8 encoded text:
Embed("data.txt", "text/plain")
  • The following results into a ByteArray:
Embed("data.bin", "application/octet-stream")
  • The following results into any obtained from parsing a JSON file:
Embed("data.json", "application/json")
  • The following embeds a file from artifacts. For example, these files may result from a build script that writes to the import.meta.env.TARGET directory in ZoneGFX applications.
Embed("{target}/data.bin", "application/octet-stream")
  • For the ZoneGFX platform, the following results into an import("zone.skins").StyleSheetFile that may be used in <z:Style> tags with the file= attribute.
Embed("Application.css", "text/css")

Enumerations

General enumerations

For general enumerations, just use a pattern like:

type Enum1 =
    | "variantA"
    | "variantB"
    ;

If the need ever arises, you can change it to a complex enumeration using the Enum() helper, detailed below.

Implementing complex enumerations

Use the Enum() helper to designate simple enumerations quickly.

Here is a basic enumeration:

const Enum1 = Enum({
    variantA: 0,
    variantB: 1,
});
type Enum1 = Parameters<typeof Enum1>[0];

// ===== usage =====

const x: Enum1 = "variantA";
const y: string = Enum1("variantB");
trace(Enum1.valueOf(x)) // 0
trace(Enum1.from(0)) // "variantA"
trace(Enum1.from("variantWrong")) // null

Here is an enumeration with a custom method:

const Enum1 = Enum({
    variantA: 0,
    variantB: 1,
}, {
    plus(e: Enum1, inc: number): number {
        return Enum1.valueOf(e) + inc;
    },
});
type Enum1 = Parameters<typeof Enum1>[0];

// ===== usage =====

trace(Enum1.plus("variantA", 1)) // 1

Type operator

Satisfies operator

type A = "a" | "b";

let a: string;
a = "a" satisfies A; // ok
a = "c" satisfies A; // ERROR

Structural versus nominal typing

EZMAScript compile-time types are fully structural, including classes.

Defining a nominal class

To define a nominal class, declare a virtual private field:

class A {
    declare private __nominal__: undefined;
}

E4X

The EZMAScript for XML (E4X) feature provides APIs for working efficiently with the XML data format. It is not be mistaken with XML-like expressions used by the ZoneDS feature of the ZoneGFX framework.

Examples

Constructing a XML object

XML(`
    <f:document>
        <f:page>
            <f:title>SínTitulo1</f:title>
        </f:page>
    </f:document>
`, [
    Namespace("f", "http://example.com/ezmascript/facade/2009"),
])