DEV Community

Michael Scharl
Michael Scharl

Posted on

How I created my first MagicMirror² module with Typescript

I recently created my first MagicMirror² module. Now I want to "talk" about how I did it.

📦 The Build Tools

As with most of my little side projects, I started from scratch by creating my build toolkit. I usually create a Webpack config install all the loaders and start building. This time I wanted to try rollup, one of the tools I had on my "I want to check this out when I have time"-list. The setup was quite simple and I like the fact that you can create multiple entries and outputs with different plugins that handle you code generation. This is ideal for this project since I want to create the core module file wich is targeted for the browser while the helper module is targeted for a node environment. In the end my config ended up looking like this.

import commonjs from 'rollup-plugin-commonjs';
import resolve from 'rollup-plugin-node-resolve';
import typescript from 'rollup-plugin-typescript';
import pkg from './package.json';

export default [
    /**
     * The core module file
     * Written in Typescript and bundled with all dependencies.
     */
    {
        input: './src/MMM-edgerouter-throughput.ts',
        plugins: [
            typescript(),
            resolve(),
            commonjs(),
        ],
        output: {
            file: './MMM-edgerouter-throughput.js',
            format: 'iife',
        },
    },
    /**
     * The module helper file
     * Written in Typescript and only compiled to be used within node.
     */
    {
        input: './src/node_helper.ts',
        plugins: [
            typescript(),
        ],
        external: ['node_helper', ...Object.keys(pkg.dependencies)],
        output: {
            file: './node_helper.js',
            format: 'umd',
        },
    },
]
Enter fullscreen mode Exit fullscreen mode

For a more convenient use I usually create three npm scripts for compiling my code. watch to continuously compile my code while developing, dev to build everything once for developing/testing and build to create a production ready package.

{
  "scripts": {
    "build": "rollup -c",
    "dev": "rollup -c --sourcemap=inline",
    "watch": "npm run dev -- --watch"
  }
}
Enter fullscreen mode Exit fullscreen mode

🏗️ The Development Environment

To ensure a fluid and easy development process I installed MagicMirror on my machine to be used in the "server-only" mode. This allows me to add every module I create as a symlink. I also created a symlink in my projects directory to the node_helper modules folder. This is not mandatory but helps my IDE figuring out what I mean when I include the node_helper.

.
├── MMM-edgerouter-throughput
│   └── …
├── MMM-oebb-station-board
│   └── …
├── …
├── MagicMirror
│   ├── …
│   └── modules
│       ├── MMM-edgerouter-throughput -> ../../MMM-edgerouter-throughput
│       ├── MMM-oebb-station-board -> ../../MMM-oebb-station-board
│       ├── …
│       └── node_modules
└── node_modules -> MagicMirror/modules/node_modules
Enter fullscreen mode Exit fullscreen mode

📘 Using Typescript

To be honest, it's not the best possible integration. The MagicMirror repository includes a module-types.ts file, containing the most important types, which I extended a little to fit my needs. A deeper integration, like being able to get correct typings/autocompletion for the this object in functions, is not available. If I find myself using it more in the future, I may look into improving this.

Beside that it works without any problems and I feel like Typescript is giving me a more robust and worriless development experience in the long run. In the end it just gets compiled to pure Javascript before being loaded into the MagicMirror. Those files need to be inside the root folder. Therefore I decided to put the Typescript files into a src folder, for a more obvious separation.

✋ Any questions.

If you have any question regarding my process or the like, feel free to ask me in the comments!

Top comments (0)