N4JS Language & IDE

Modules

Large-scale projects of almost any language require good modularization for maintenance reasons. It’s vital for projects that modules don’t become oversized and comprise a well-defined purpose that is easy to comprehend for developers. The N4JS IDE helps for keeping track of your code modules and validates their usage.

Multiple Exports

While it’s a good habit to assign one module for a single type, it may be suitable to define multiple types or variables in a single module in cases where they correspond or are related. ECMAScript 2015 allows for this with the module syntax it defines. The good news is that N4JS already supports most of it:

export public function inc(): void { ++count; }
export public var count = 5;
export default public class Foo {
    public callOnIt(): void { }
}

Importing an ECMAScript 2015 module and consuming exports is as easy as the following:

import { inc, count } from 'Counter';// Named imports
import Foo from 'Counter'; // Default import
import { readFile } from 'fs' // Node.js core modules
import express from 'express';  // npm modules
import * as plainJsModule+ from 'plainJsModule'; // Dynamic import

The module could also be imported as a whole:

import * as foo from 'Counter'; // Namespace import
Anonymous default exports and relative module paths are not yet supported. The module specifier is the relative path of the module in its source folder. Optionally the project name can be used as prefix, e.g., in case of ambiguities.

N4JS IDE Organizes Imports

Manually writing import statements is not necessary: the N4JS IDE does this automatically. The IDE will recognise all projects involved via various runtime and library dependencies that are defined in by the user in the manifest.n4mf file. Simply write the type or variable you require and press Ctrl+SPACE. Content assist within the IDE will add the missing import statement for you.

moduleimport

When pasting in a code snippet, you could use Ctrl+Shift+O (on Windows and Linux, Alt+Cmd+O on Mac) which organizes your imports and adds any missing imports. This is also used to clean up any unused imports.

Import Removal

When developing in N4JS, you may notice that you sometimes need to import types to cast a variable or use it for a constraint in a generic.

import Foo from 'Counter';

var someOtherVar: Object;
(someOtherVar as Foo).callOnIt();

export function <T extends Foo> bar(t: T): void { }

The compiler will notice that there’s no runtime code dependency on the imported module and will omit loading the module.

Read-only Views

ES2015 modules have the concept of read-only views. Although you cannot modify anything imported from a module, your binding is live. In the case that the exporting party has modified the export, you will read the current value. For example:

import {inc, count} from 'Counter';

console.log(count); // 5
inc();
console.log(count); // 6

Although highly discouraged, read-only views allow to support some cyclic dependencies across modules. N4JS might even reorder type definitions to achieve this.

Loader Details

So far, there’s no engine/platform that has implemented the ES2015 specification natively. N4JS and most other transpilers, transpile modules into the SystemJS format.

N4JS' output format goes a bit further and emits an unified format that works as well in a synchronous CommonJS/Node.js environment. Keep in mind, however, that when you load a module the CommonJS way, you are limited to this with regard to cyclic dependencies because it has to resolve all dependencies synchronously.