Module Layer
This section defines what a JavaScript OSGi module (bundle) is and how it is structured.
A bundle is the OSGi term for a module and defines a minimal structural unit within the OSGi runtime. An app built on top of the JS OSGi runtime consists of one or more bundles that contain code and resources to build up the whole app. A bundle typically solves one concrete domain problem or provides a special tool. An OSGi bundle is normally reused in multiple applications.
The JavaScript OSGi runtime is based on Dojo Toolkit (http://dojotoolkit.org/) which is using the "Asynchronous Module Definition (AMD)" format (http://dojotoolkit.org/documentation/tutorials/1.7/modules/) since version 1.7. This format defines how source files are structured and how they are loaded. Base knowledge about this mechanism is required to fully understand the content of this page. As of map.apps 4.x next to the AMD module syntax the ES6 module syntax is supported but has to be transpiled to the AMD format. Within the documentation most of the samples use the new ES6 syntax. |
A typical bundle has following structure:
\- <bundle-folder> // equals the name of the bundle
+- nls // i18n directory (see Dojo i18n), optional
| +- de // German i18n resource directory
| | \-bundle.js // German i18n resource file
| \- bundle.js // root i18n resource file (en)
|
+- manifest.json // bundle metadata description (required)
// instead of a manifest.json file, a package.json file is also accepted
|
+- main.js // AMD package main file, normally used to trigger i18n loading (optional)
+- module.js // Bundle-Layer, main trigger for loading dependent JS files (optional)
+- AClass.js // an implementation class with certain functionality (optional)
+- AActivator.js // an Activator implementation class (optional)
The important thing is the manifest.json
file.
The manifest.json
file provides some meta information and defines the functionality provided by the bundle.
A minimal manifest.json
file might look like this:
manifest.json
file{
"name": "mybundle",(1)
"version": "1.0" (2)
}
1 | The official name of the bundle, this should match the name of the folder (required). |
2 | The version identifier of the bundle (required). |
The name is used as an identifier of the bundle within the configuration and loading system and must be unique among all bundles. Each bundle has its own version number. It should be incremented if the bundle is changed.
Version Identifiers
The syntax of version identifiers in map.apps follows the specification defined in Semantic Versioning 2.0 .
This means that any version consists of the three mandatory components:
-
MAJOR
-
MINOR
-
PATCH
The components are separated by a single period ('.').
Valid: 3.1.0
Invalid: 3.1 (missing PATCH)
Version identifiers can be extended by a pre-release identifier appending a hyphen and a series of dot-separated identifiers.
3.1.0-alpha
3.1.0-alpha.1
Additionally, version identifiers can be extended by a build metadata identifier appending a plus sign and a series of dot-separated identifiers.
3.1.0+20141230.r320964
3.1.0-alpha.1+20141230.r320964
We recommend to use version numbers as specified in Semantic Versioning 2.0 on the semantical level, too. Citing the specification summary, this means:
Increment the:
MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backward compatible manner, and
PATCH version when you make backward compatible bug fixes.
The semantic versioning implementation of map.apps has some limitations and special cases. map.apps supports a 4th version number, the hot-fix version (for example All version components (major, minor, patch and hot fix) must be between The following list explains the rules using examples:
In map.apps versions before 4.13 the |
Dependencies on other Bundles
If a bundle requires another bundle to be installed in the app to work correctly, this requirement should explicitly be expressed in the manifest.json
file:
{
...
"dependencies": {
"map" : "~4.0.0", // <name> : <version range expression>
"dojo" : "1.11.1"
},
...
"optionalDependencies": {
"coordinatetransformer" : "^4.0.0"
},
...
}
The JS OSGi runtime tests if the required bundles are installed and ensures that theses bundles are started before the bundle that is declaring the dependency. If a bundle has an implementation class dependency to another bundle (if it needs a class of another bundle), declare this dependency to ensure that the needed class is loaded before the bundle is started.
In contrast to the Java OSGi specification, dependencies to other bundles are directly expressed using the bundle name. The JS OSGi implementation provides no package-level dependency expressions (Export-Packages, Import-Packages). Also the same bundle cannot be loaded in different versions in the same page. |
Version Ranges
Different styles to specify version ranges can be used inside the dependencies declarations.
Since map.apps 3.1 two different styles of version range expressions exist:
-
OSGi-like: This style, based on the OSGi specification, is the only one available before map.apps 3.1.
-
npm-like: Introduced with the JS Registry in map.apps 3.1 this style uses the version range syntax defined by the semver tool of the Node Package Manager (npm).
OSGi-like Version Ranges
This style of defining version ranges is deprecated with map.apps 3.1.
Version ranges are expressed by an interval syntax:
versionrange := '(' | '[' + version + ',' + version + ')' | ']';
version := major + '.' + minor + '.' + bugfix + '-'|'.' + POSTFIX;
major := an integer number >= 0
minor := an integer number >= 0 optional
bugfix := an integer number >= 0 optional
POSTFIX := a string optional
Brace | Description |
---|---|
|
left opened not including interval |
|
left closed including interval |
|
right opened not including interval |
|
right closed including interval |
Sample | Description |
---|---|
|
Exactly version 2.0.0 is required |
|
Version 1.0.0 till 2.0.0 are allowed (including version 1.0.0 and 2.0.0) |
|
All versions between 1.0.0 and 2.0.0 are allowed (not including the version 1.0.0 and 2.0.0) |
|
All versions later than or equal to 1.0.0 and earlier than 2.0.0 are allowed (including 1.0.0 and not including 2.0.0) |
|
All versions later than or equal to 1.0.0 are allowed (including 1.0.0) |
|
All versions are allowed (the default) |
|
All versions later than 0.0.0 and earlier than 1.0.0 are allowed. |
A version consists of the well known parts, major number, minor number, bug fix number and a post fix. Only the major part must be defined, all other parts are optional.
A version is compared to another version using the following rules:
-
compare the major number part, only if equal go to step 2
-
compare the minor number part, only if equal go to step 3
-
compare the bug fix number part, only if equal go to step 4
-
compare the pre-release post fix part, using string compare. A version with a pre-release postfix is considered earlier than the version without that postfix.
Sample | Description |
---|---|
|
Major part is different |
|
Minor part is different |
|
Bug fix part is different |
|
Special postfix SNAPSHOT is interpreted as pre-release version |
|
Special postfix PREVIEW is interpreted as pre-release version |
|
A postfix can have its own incremental number part |
|
You can skip the minor, bugfix, and postfix part |
|
You can skip the minor, bugfix, and postfix part |
npm-like Version Ranges
Specific Versions
|
Specific version 1.2.3. Build metadata is still ignored, so "1.2.3+build2012" satisfies this range. |
Ranges
|
Greater than 1.2.3 version. |
|
Less than 1.2.3. If there is no prerelease tag on the version range, no prerelease version is allowed, even though these are technically "less than". |
|
Greater than or equal to 1.2.3. Prerelease versions are NOT equal to their "normal" equivalents, so 1.2.3-beta does not satisfy this range, but 2.3.0-beta will. |
|
Less than or equal to 1.2.3. In this case, prerelease versions ARE allowed, so 1.2.3-beta satisfies. |
Intersections of Ranges
Ranges can be joined by whitespace to express an intersection ("and") of ranges.
|
Greater than or equal to 3.1.0 and less than 4.0.0, so 3.2.0 is satisfied. |
|
Equivalent to >=3.1.0 ⇐4.0.0, using special "hyphen" syntax |
If ranges are joined using '||' (double pipe) it is interpreted as "or".
|
Version 3.1.0 or 3.1.1 |
|
"3.1.0" or "greater or equal to 3.1.2 but less than 3.2.0" |
Advanced Syntax
|
Equivalent to >=1.2.3-0 <1.3.0-0. "Reasonably close to 1.2.3". When using tilde operators, prerelease versions are supported as well, but a prerelease of the next significant digit does NOT satisfy, so 1.3.0-beta does not satisfy ~1.2.3. |
|
Equivalent to >=1.2.3-0 <2.0.0-0. "Compatible with 1.2.3". When using caret operators, anything from the specified version (including prerelease) is supported up to, but not including, the next major version (or its prereleases). 1.5.1 satisfies ^1.2.3, while 1.2.2 and 2.0.0-beta does not. |
|
Equivalent to >=0.1.3-0 <0.2.0-0. "Compatible with 0.1.3". 0.x.x versions are special: the first non-zero component indicates potentially breaking changes, meaning the caret operator matches any version with the same first non-zero component starting at the specified version. |
|
Equivalent to =0.0.2. "Only the version 0.0.2 is considered compatible". |
|
Equivalent to >=1.2.0-0 <1.3.0-0 "Any version starting with 1.2". |
|
Equivalent to >=1.2.0-0 <2.0.0-0 "Any version compatible with 1.2". |
|
Equivalent to >=1.2.0-0 <1.3.0-0 "Any version starting with 1.2". |
|
Equivalent to >=1.0.0-0 <2.0.0-0 "Any version starting with 1". |
|
Equivalent to >=1.0.0-0 <2.0.0-0 "Any version compatible with 1". |
|
Equivalent to >=1.0.0-0 <2.0.0-0 "Any version starting with 1" |
|
Equivalent to >= 0.0.0. Any range. |
The OSGi format is also supported:
[1.0,) := >=1.0.0 (1.0,) := >1.0.0 (,1.0] := <=1.0.0 (,1.0) := <1.0.0 [1.0,2.0] := >=1.0.0 <=2.0.0 [1.0,2.0) := >=1.0.0 <2.0.0 (1.0,2.0] := >1.0.0 <=2.0.0 (1.0,2.0) := >1.0.0 <2.0.0 (1.0,2.0) := >1.0.0 <2.0.0
Internationalization (i18n)
Add the following files to internationalize a bundle:
\- <bundle-folder>
+- nls // i18n directory (see dojo i18n) optional
+- de // German i18n resource directory
| \-bundle.js // German i18n resource file
\- bundle.js // root i18n resource file (en)
The file structure is based on the dojo/i18n module explained at http://dojotoolkit.org/reference-guide/1.8/dojo/i18n.html.
The file /nls/bundle.js
(root i18n file) contains the English resources, like:
// AMD
define({
root: {
bundleName: "My Bundle",
bundleDescription: "The bundle provides some functionality."
},
"de": true, // German supported
... // list supported languages
});
// ES6
export default {
root: {
bundleName: "My Bundle",
bundleDescription: "The bundle provides some functionality."
},
"de": true
};
The file /nls/de/bundle.js
(German i18n file) contains the German resources, and does not need the root property:
//AMD
define({
bundleName: "Mein Modul",
bundleDescription: "Das Modul bietet Funktionen."
});
// ES6
module default {
bundleName: "Mein Modul",
bundleDescription: "Das Modul bietet Funktionen."
};
To trigger loading of the nls files, a add the line dojo/i18n!./nls/bundle.js
to the dependencies of the main.js
file:
//AMD
define(["dojo/i18n!./nls/bundle"],{});
//ES6
import "dojo/i18n!./nls/bundle";
With enabled i18n support you can use i18n key replacement in manifest.json
files.
You can use expressions like ${i18nkey}
to access i18n properties in the manifest.json
file.
The next sample shows how to use a language-aware title:
manifest.json
file{
...
"title": "${bundleName}",
...
}
To access i18n properties from JavaScript code, see Module LifeCycle for a sample use in Activators and Declarative services for use in components.
To disable i18n support, set an empty array in the manifest.json
file:
manifest.json
file{
...
"i18n" : [], // disables i18n support for this bundle
...
}
The default declaration of the "i18n"
property is:
manifest.json
file{
...
"i18n" : ["bundle"],
...
}
So you can change the default name of the nls files by modifying this property, but we do not recommend it.
Bundle JS Loading
By default a bundle has to provide a file named module.js
that is loaded during start of the bundle.
This file is considered to be the bundle’s "JavaScript Layer" file, which means that it triggers loading all JavaScript files required by the bundle.
The name of the file can be changed using the "layer"
property.
manifest.json
file{
...
"layer": "module"
...
}
To disable resolving of a layer file, declare the property with an empty string:
manifest.json
file{
...
"layer": "" // disables the resolving of any JavaScript file
...
}
A module.js
file typically looks like this:
//AMD
define([
".",
"./MyClass"
],{});
//ES6
import ".";
import "MyClass";
In the following, the loading behavior and class name resolving are explained by an example.
Classloading Example
Consider the following bundle files:
\- <bundle-folder>
+- /nls/bundle.js //i18n
+- manifest.json
+- main.js
+- module.js
+- Activator.js
With the content:
{ ...
// An instance of class Activator should be created and informed about the bundle start and stop
"activator": "Activator",
...
}
// AMD
// trigger load of i18n files
define(["dojo/i18n!./nls/bundle"],{});
//ES6
import "dojo/i18n!./nls/bundle";
// AMD
define(["dojo/_base/declare"], function(declare) {
// declare the class Activator
return declare([], {
start: function(){},
stop: function(){}
});
});
// ES6 (use default exports)
export default class {
start(){}
stop(){}
}
// ES6 (use named exports, requires that the file is named Activator.js)
export class Activator{
start(){}
stop(){}
}
// AMD
define([
// trigger load of main.js
".",
// trigger load of Activator.js
"./Activator"
],{});
// ES6
import ".";
import "./Activator";
Each bundle is registered as an AMD package using the "name"
value during loading.
This means that all source files within a bundle should reference each other using relative path expressions in the AMD module declaration (define statement).
If inside the define
statement the special value "."
is used, the AMD loader loads the file main.js
.
The module.js
file is used to trigger loading the files main.js
and Activator.js
.
During the start of the bundle the JS OSGi runtime checks the "activator"
property and resolves it using a require
statement.
However, this does only work if the class is already loaded.
Restrict a bundle to a specific environment
A bundle can be restricted to a special execution environment using the following properties in the manifest.json
file.
If a browser does not match the execution requirement rules, the bundle is not started.
manifest.json
file{
...
// Bundle supports Internet Explorer from version 7 and Firefox from version 3
"requiredExecutionEnvironment": ["IE:[7,]","FF:[3,]"],
...
// Bundle does not supports Opera Browsers
"excludedExecutionEnvironment": ["Opera"]
...
}
The property "requiredExecutionEnvironment"
defines a list of supported browsers while "excludedExecutionEnvironment"
defines a list of unsupported browsers.
Both properties are optional.
Each entry in the array of browsers has the following format:
manifest.json
filevalue := <browsername>(:<version-range>)?
Samples | Description |
---|---|
|
Firefox from version 3.1 to 4.0 |
|
Firefox from version 1.0 to 4.0 (4.0 not included) |
|
Firefox any version |
|
Firefox from version 3 and later |
Following browser names can be used:
Name | Description |
---|---|
|
Internet Explorer |
|
Microsoft Edge |
|
Firefox |
|
Google Chrome |
|
KHTML browsers |
|
Mozilla-based browsers (Firefox, SeaMonkey) |
|
Opera |
|
Safari based browsers, Safari or iPhone |
|
WebKit based browser (such as Konqueror, Safari, Chrome) |
|
Android |
|
iPhone |
|
iPad |
|
BlackBerry |
|
Browser supports touch input |
|
The browser is a mobile variant |
manifest.json
file properties
The following table gives an overview of all properties that can be used in a manifest.json
file:
Property | Optional | Default | Description |
---|---|---|---|
|
no |
The symbolic name of this bundle |
|
|
no |
The version of this bundle |
|
|
yes |
Value of |
The display name of this bundle |
|
yes |
|
The vendor of this bundle. |
|
yes |
|
A description of this bundle. |
|
yes |
|
A list of categories (application specific use, tags for the bundle). |
|
yes |
|
A contact address for this bundle. |
|
yes |
|
The copyright of this bundle. |
|
yes |
|
A documentation link. |
|
yes |
|
Metadata about the icon of this bundle. |
|
yes |
|
Metadata about the license of this bundle. |
|
yes |
|
Specifies the name of the source file, which is loaded during the resolve phase of the bundle. This file is very important for bundle loading and source/component name resolution. |
|
yes |
|
Defines the StartLevel of this bundle. This provides a way to modify or optimize the start order of bundles. |
|
yes |
|
Defines that this bundle shall be started or not. |
|
yes |
|
Specifies a bundle activator class. This activator is informed about the start and stop of the bundle. |
|
yes |
|
Specifies if the bundle provides i18n, see Internationalization. |
|
yes |
|
Specifies client environment requirements. If this requirements are not fulfilled, the bundle is not started. |
|
yes |
|
Excludes a execution environment, works like |
|
yes |
|
Declares the dependencies of this bundle to other bundles. |
|
yes |
|
Declares the dependencies of this bundle to other bundles. |
|
yes |
|
This is the entry point for component declarations, see Declarative services. |