Installing a TypeScript application on a YoctoHub

Installing a TypeScript application on a YoctoHub

The TypeScript language is a great improvement on JavaScript, as it allows for error detection during compilation rather than during run time. But if it's quite easy to integrate with Node.js, its use to write HTML interfaces without a Node.js server is not so trivial. We are therefore going to show you how to implement an HTML application in TypeScript which optimizes network transfers, so as to install it directly on a YoctoHub-Ethernet.


As a concrete example, we are going to revisit in TypeScript and with today's technologies the web interface to drive a roller shutter example that we implemented in 2019.

Structure of the project

Unlike the original example where the JavaScript code could be inserted directly in the HTML page, it's not possible to put TypeScript code directly in an HTML file: browsers don't know how to compile it. There are tricks to load a TypeScript compiler into an HTML page, but this greatly increases the quantity of code to transfer and it's therefore not interesting for us. An HTML application in TypeScript has thus generally the following minimal structure:

  • one or several HTML pages
  • one or several TypeScript files (.ts)
  • a tsconfig.json file, with the TypeScript compilation options

The HTML page does't directly reference the source files in TypeScript, but the compiled version in JavaScript (.js). And unlike what happened before the apparition of module systems, the page doesn't need to explicitly include all the JavaScript files, but only the entry point. The other modules are automatically loaded as dependencies, depending on the import orders that they contain. This is what the new HTML file of our application looks like:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Roller Shutter Control</title>
    <!-- loading the compiled TypeScript code -->
    <script type="module" src="app.js"></script>
</head>
<body>
<div id="mainUI" style="position: absolute; left:50%; top: 50%; text-align: center; font-family: sans-serif; width:30ex;">
    <span id="UP"></span><br>
    Roller shutter control<br>
    <span id="DOWN"></span>
</div>
</body>
</html>



Note the inclusion of the JavaScript script with the type="module" tag. It fundamentally changes the way in which the included JavaScript code is dealt with. Unlike a JavaScript file included in the traditional way, a JavaScript module doesn't define global symbols, to respect the encapsulation between modules and to avoid name conflicts. Each module can however import dependencies, and thus access to symbols that it has explicitly imported. The browser automatically and dynamically loads dependencies, which wasn't possible in a traditional JavaScript file.

Note, for this module loading mechanism to work, you must imperatively compile the TypeScript code so that it produces an ECMAScript 2015 module, with the "module": "es2015" option in the tsconfig.json file.

To deploy this application on a YoctoHub-Ethernet, you must:

  1. install TypeScript if it wasn't already done, as described in this post;
  2. compile the TypeScript files by running the tsc command;
  3. copy only the HTML file(s) and the JavaScript file(s)

Here's how our roller shutter control application is loaded, as is:

The browser loading ECMAScript 2015 modules
The browser loading ECMAScript 2015 modules



Bundling

By observing the transfers when loading the application, we notice that the transfer of the JavaScript classes of the Yoctopuce API take most of the time, and that it would be possible to reduce the loading time by compressing them.

To compress JavaScript code, we use to complementary methods:

  1. minification, which consists in suppressing spaces and shortening the name of local symbols
  2. compression per se, which consists in transferring the code as a binary compressed stream rather than using a text format, much heavier.


However, binary compression works best when the processed file is large. It is therefore advisable to add an intermediary step which gathers all the JavaScript files required for the project into a single large file, which is called bundle. Having only one JavaScript file also simplifies the installation. But, unfortunately, one can't simply put ECMAScript modules end to end as we would have done with traditional JavaScript code: we must perform a transformation, which corresponds to the resolution of the imports/exports between the modules.

There are many tools able to manage this import resolution and which manage minification at the same time: WebPack, rollup.js, parcel, ... These tools existed before the advent of ECMAScript modules and were used for other types of JavaScript modules. But, because of this, they have a tendency to complicate things, by adding code to the project to dynamically solve dependencies when, on the contrary, we would like to simplify them. What we would like is simply a linker for ECMAScript modules, as this exists for traditional compiled languages.

Luckily, we are not the first ones to look for such a tool: we finally found a small, simple and efficient tool called esbuild which does exactly what we want. And in the opposite to WebPack which drags with it a hundred or so dependencies, esbuild needs nothing but itself to work, and it's very fast.

To create our minified bundle, we only need to install esbuild and to run the following command in the same directory:

esbuild app.js --bundle --sourcemap --minify --outfile=app.min.js


The --sourcemap argument enables us to create a app.min.js.map file which, if it is present on the server, enables us to debug directly in the source file rather than in the unreadable minified version.

Note that we have asked esbuild to create the bundle from the app.js file, and not from the app.ts TypeScript file. One might be tempted to do so since esbuild natively supports TypeScript, but unfortunately esbuild does not support the const enum types of TypeScript. Therefore, the compiled code is not compatible with the TypeScript libraries compiled with the official compiler, such as the Yoctopuce library. It's the only shortcoming that we encountered with this tool, but we can easily work around it by simply running tsc before creating the bundle.

Compression

If your application is designed to work from a traditional web sever, such as Apache, you only need to put the app.min.js file and the HTML file (referring to app.min.js instead of app.js) on the server. The transfer of the JS file is performed automatically in compressed mode if the server is properly configured, for example with mod_deflate.

Unfortunately, YoctoHubs don't have the computation power enabling compression on the fly. If you want your application to be transmitted compressed by a YoctoHub-Ethernet or a YoctoHub-Wireless-n, you must pre-compress the bundle in the GZIP format (.gz extension) before loading it on the YoctoHub.

Under Linux, this is done with the following command line:

gzip -9 app.min.js


and you get a app.min.js.gz of the minimum size.

Under Windows, instal the free 7-Zip tool and right-click on the app.min.js file. In the 7-Zip submenu, select the Add to archive... option to open 7-Zip. Select the gzip format, the Ultra compression level, and click on OK.

You then only need to put the HTML file and the app.min.js.gz file on the YoctoHub and your application is ready to run. When the browser tries to load the app.min.js file, the YoctoHub notices that it's not there as is but in GZIP format and sends it with a corresponding Content-Encoding so that it is transparent for the browser. Note, this feature was added recently, so take care to update the firmware of your YoctoHub before trying.

Result

Here is the trace of the same application loading, this time bundled with esbuild and compressed:

The browser loading the compressed bundle
The browser loading the compressed bundle


As expected, compressing JavaScript code is really worth it... we gain more than a factor of 10 in loading time!

You can find all the files of this example in the Yoctopuce TypeScript library, in the example_html/Prog-Bundle subdirectory. We added to it a package.json file enabling you to locally install esbuild as a development dependency. You can thus use the following commands:

npm install to install the dependencies (esbuild and the Yoctopuce library)
npm run build to compile and build the bundle

Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.