Starting with the LuciadRIA 2023.0 release, LuciadRIA modules fully comply with the ECMAScript Modules (ESM) resolution algorithm used by browsers, module bundlers and NodeJS. This means that browsers can now load LuciadRIA modules with or without the use of a bundler. For consistency, we converted all NodeJS build scripts in the LuciadRIA release to ESM. This article first outlines the benefits of using ES Modules. Then, it describes the changes in LuciadRIA, and how you can upgrade to LuciadRIA 2023.0 in your project.
Benefits of ES Modules in the browser
Most browsers support the ES module syntax — import
and export
— for quite some time now, but they still lacked some features for the loading of those modules until recently.
One missing feature was import-maps
. The import-maps
feature allows you to control what URLs get fetched by JavaScript import
statements and import()
expressions.
For example, you can use an import map to map @luciad/ria/…
imports to LuciadRIA modules hosted on a server:
<!DOCTYPE html> <html lang="en"> <head> <script type="importmap"> { "imports": { "@luciad/ria/": "/lib/ria/" } } </script> </head> <body style="margin: 0; padding: 0;"> <div id="map" style="width: 100vw; height: 100vh;"></div> <script type="module" src="./main.js"></script> </body> </html>
import {WebGLMap} from "@luciad/ria/view/WebGLMap.js"; const map = new WebGLMap("map", {reference: "EPSG:4978"});
Note that the import identifier includes a file extension .js
.
Unlike bundlers, that use the NodeJS CommonJS module resolution, browsers require you to include the file extension in your
imports.
As of 2023.0, LuciadRIA modules include the file extension in the API and in samples.
This allows the native loading of these modules by the browser, even without using a bundler.
Note that bundlers, such as webpack, can also handle import identifiers that end with a file extension.
This means that you can use the same main.js
code in the browser, with or without a bundler.
Being able to load ES modules in the browser natively is great, but the use of bundlers still offers some benefits. Loading many small modules over the network can be slower than loading a single, bundled script, because each network request has a bit of overhead. For production usage, you may want to bundle your modules into a single file. Bundling tools also offer other features like:
|
ES Modules in NodeJS
NodeJS supports two module systems: CommonJS (CJS) and ECMAScript Modules (ESM). For consistency with the LuciadRIA API and sample modules, we converted the NodeJS build scripts in a LuciadRIA release to ESM in 2023.0.
LuciadRIA’s API is intended for use in a browser environment, not in NodeJS. LuciadRIA assumes the presence of browser APIs, like the DOM API, WebGL, and others. These aren’t available in NodeJS. |
The adoption of ECMAScript Modules by Node.js reflects a broader shift in the JavaScript ecosystem towards ESM. Several factors motivated this shift:
-
Standardization: ESM is the official standard for JavaScript modules as specified by ECMAScript, the standardized specification of JavaScript. By supporting ESM, Node.js aligns itself with this standard, which helps improve interoperability and reduces fragmentation in the JavaScript ecosystem.
-
Static Analysis: ESM supports static imports and exports, which can be analyzed at compile-time, as opposed to at run-time with CommonJS. This can lead to performance improvements, because tools can perform optimizations like tree-shaking to remove unused code. It can also lead to better tooling support in IDEs for features such as auto-imports and "go to definition" for example.
-
Browser Compatibility: Modern browsers support ESM. Therefore, using ESM can make it easier to write isomorphic code — code that can run both in Node.js and in the browser — without needing a build step or special tooling to convert module formats.
-
Asynchronous Loading: ESM has built-in support for asynchronous loading and can handle circular dependencies more predictably than CommonJS.
-
Features: ESM supports features that CommonJS doesn’t, such as top-level await and importing JSON or native modules.
These benefits motivated Node.js to add support for ESM, but it’s important to note that Node.js didn’t remove support for CommonJS. As of now, Node.js supports both module systems to maintain backward compatibility and to allow developers to choose the system that best suits their needs.
What changed in LuciadRIA 2023.0?
As of LuciadRIA 2023.0, all JavaScript modules in the release are in ESM format. This includes API modules, sample modules, and NodeJS build scripts.
This is an overview of the changes:
-
All imports in the API and in sample code now include the file extension. This allows both bundlers like webpack and the browser’s native module loader to load LuciadRIA modules.
-
All
package.json
files now declare"type": "module"
. This declaration makes NodeJS assume that.js
files are ES modules, instead of CommonJS modules. -
All NodeJS build scripts are now ES modules instead of CommonJS files. This includes webpack configuration files (
webpack.config.js
). To maintain compatibility with CommonJS projects that usetoolbox/ria/config/webpack.config.js
, we ensured that this file can still be imported withrequire()
. -
The TypeScript configuration in sample code now uses the
nodenext
module resolution.
Upgrading to 2023.0
When upgrading to 2023.0, you can choose to stick with CommonJS modules, or switch to the new ES modules.
If you’ve based your project on a LuciadRIA sample, or if you’re using |
Stick with CommonJS modules
Sticking with CommonJS modules is relatively straightforward. Upgrading to a new version of the API itself shouldn’t give any issues.
If you’re using TypeScript and:
you must revert back to the old
|
Switch your project to ES Modules
If you also want to switch to ES modules in your project, you can:
-
Update your
package.json
: ensure that the"type"
field in yourpackage.json
is set to"module"
.This tells Node.js to treat.js
files as ES modules. -
Convert your NodeJS scripts to ES modules: replace
require()
imports withimport
statements, and replacemodule.exports
declarations withexport
. You can check out the LuciadRIA 2023.0 sample NodeJS scripts for inspiration, for examplesample/common/webpack.config.js
.You can use a tool like
cjstoesm
to automate part of the CJS-to-ESM conversion. -
Add file extensions to your imports: File extensions are mandatory when you’re using ES Modules resolution.Update your imports to include file extensions. For example:
import {getReference} from "@luciad/ria/reference/ReferenceProvider"; import {SingleMapSample} from "@luciad/ria-sample-common-ui/SingleMapSample"; becomes
import {getReference} from "@luciad/ria/reference/ReferenceProvider.js"; import {SingleMapSample} from "@luciad/ria-sample-common-ui/map/SingleMapSample.jsx"; Even in TypeScript, the file extension of the imported module is
.js
or.jsx
(not.ts
or.tsx
).
You can use the LuciadRIA 2023.0 sample code as a reference. Specifically, you can look at the package.json
, the updated imports, and the updated webpack.config.js
files.