Espruino plugin for Rollup module bundler

Posted on
  • I've made a plugin for Rollup that lets you push the resulting code straight to Espruino devices.

    Rollup is a module bundler, which takes JS modules (preferably ES modules) and bundles them into one file. While doing so, it employs tree-shaking and static analysis to remove unused code, resulting in a file that contains only the bare minimum. Perfect for Espruino :)

    It supports plugins, so you can easily add minification of code, linting, support for CoffeScript – and thankfully, support for CommonJS modules, which is the format of Espruino's modules.

    Basically, this allows you to structure your Espruino projects using modern ES modules, and have Rollup bundle it all up and send the resulting code to Espruino in one command.

    I've only tested it on Puck.js so far, but it's based on code from EspruinoTools' CLI, so hopefully it works for all supported devices. There's also one more feature I'd like to add, the ability to save after sending.

    If this sounds useful, try it out and let me know how it works (or doesn't work) :)

    npmjs.com/package/rollup-plu­gin-espruino

  • In order to use Espruino modules (and other CommonJS modules) a few extra steps are required, especially if you also want to uglify/minify the code. This assumes you already have Node, npm and Rollup installed.

    First, install the plugins…

    npm install rollup-plugin-espruino
    npm install rollup-plugin-commonjs
    npm install rollup-plugin-uglify
    

    You need to use the "harmony" branch of the uglify-js library for the uglify plugin to support ES modules. This will fetch it from GitHub:

    npm install mishoo/UglifyJS2#harmony
    

    Finally, add the following configuration to your Rollup config (rollup.config.js if using the CLI):

    import espruino from 'rollup-plugin-espruino'
    import commonjs from 'rollup-plugin-commonjs'
    import uglify from 'rollup-plugin-uglify'
    import { minify } from 'uglify-js'
    
    export default {
      entry: 'src/main.js',
      dest: 'dist/bundle.js',
      format: 'es',
      plugins: [
        commonjs(),
        uglify({}, minify),
        espruino(),
      ],
    }
    

    Here I'm passing the "harmony" branch of uglify-js as minify to the uglify plugin.

    And with that, it should work as expected. In harmony.

    Luckily, other plugins are more straight forward.

  • And with that configuration, here's the DS18B20 module's example rewritten in modern JS:

    import { connect } from './modules/DS18B20.js'
    
    let ow = new OneWire(A1)
    let sensor = connect(ow)
    
    setInterval(() => {
      sensor.getTemp(temp => console.log('Temp is ' + temp + '°C'))
    }, 1000)
    

    The same code bundled with the module, uglified and sent to my Espruino after running rollup -c:

    function DS18B20(t,e){if(this.bus=t,void 0===e?this.sCode=this.bus.search()[0]:pa­rseInt(e).toString()==e&&e>=0&&e<=126?th­is.sCode=this.bus.search()[e]:this.sCode­=e,!this.sCode)throw new Error("No DS18B20 found");this.type=parseInt(this.sCode[0]­+this.sCode[1])}var C={CONVERT_T:68,COPY:72,READ:190,WRITE:7­8};DS18B20.prototype._r=function(){var t=this.bus;return t.select(this.sCode),t.write(C.READ),t.r­ead(9)},DS18B20.prototype._w=function(t,­e,s){var r=this.bus;r.select(this.sCode),r.write(­[C.WRITE,t,e,s]),r.select(this.sCode),r.­write(C.COPY),r.reset()},DS18B20.prototy­pe.setRes=function(t){var e=this._r();t=[31,63,95,127][E.clip(t,9,­12)-9],this._w(e[2],e[3],t)},DS18B20.pro­totype.getRes=function(){return[31,63,95­,127].indexOf(this._r()[4])+9},DS18B20.p­rototype.isPresent=function(){return this.bus.search().indexOf(this.sCode)!==­-1},DS18B20.prototype.getTemp=function(t­){function e(e){for(var s=e._r(),r=0,i=0;i<8;i++){r^=s[i];for(va­r o=0;o<8;o++)r=r>>1^140*(1&r)}var n=null;return r==s[8]&&(n=s[0]+(s[1]<<8),32768&n&&(n-=­65536),n/=10==e.type?2:16),t&&t(n),n}if(­this.bus.select(this.sCode),this.bus.wri­te(C.CONVERT_T,!0),!t)return e(this);var s={9:94,10:188,11:375,12:750};setTimeout­(e,s[this.getRes()],this)},DS18B20.proto­type.searchAlarm=function(){return this.bus.search(236)},DS18B20.prototype.­setAlarm=function(t,e){t--,t<0&&(t+=256)­,e<0&&(e+=256);var s=this._r();this._w(e,t,s[4])};var connect=function(t,e){return new DS18B20(t,e)};let t=new OneWire(A1),e=connect(t);setInterval(()=­>{e.getTemp(t=>console.log("Temp is "+t+"°C"))},1e3);
    

    Not so pretty anymore..

  • Wow, thanks - this looks great! It might well be able to minify it even further as well (changing the name of DS18B20 for instance).

    So is it also able to fetch the modules from the Espruino website as well?

    If you want to do save, you might just be able to set Espruino.Config.SAVE_ON_SEND to 1 before sending.

  • Yea, I noticed that it could be minified even further. That's up to the Uglify plugin to do, I'll see whether it can be configured to be more ugly.

    At the moment you have to manually download the modules, but I think it might be possible to automatically download Espruino modules like the Web IDE does, I'll see what I can do! Would be very nice if this could work just like the Web IDE, that's a good benchmark :)

    I don't think setting SAVE_ON_SEND would work, as I'm skipping the part of EspruinoTools that does minification, which is what calls the plugin for saving, if I recall correctly. Can I call that plugin directly somehow?

  • If you want to skip minification (which shouldn't be on by default anyway?) you can set the config variables for it - you probably shouldn't try and cut bits of the pipeline as there might well be some other stuff in there that's required (like the newline fiddling)...

  • You're right of course! I think I skipped the transformForEspruino processor call when I was trying to get things to work, and honestly hadn't really understood how the plugin system works until now. Now that the processor call is back my code follows the same pipeline as sendCode, and it seems to be doing just the same (I'm not calling it directly because I want more control over output).

    Some more changes:

    • Better options
      • Now supports reset, save, setTime, BLE, audio, baudRate and throttle (all with Espruino's defaults)
      • Any other Espruino config options may be set using their uppercase names directly
      • Added output option for showing the output from Espruino when sending code
    • Detects and reports any uncaught JS errors after code has been sent (very useful)
    • Prettier CLI with colours and all

    I also figured out how to minify it even further, and added a tip for how to configure Uglify to be even uglier on the project page.

  • I've looked into loading Espruino modules straight from espruino.com/modules, and while that seems possible, I think It will have to wait untill I actually need is, I have enough on my plate as it is.

    If someone else wants to give it a shot, the relevant Rollup hooks appear to be resolveId and load.

  • I made a noob mistake when publishing the package to NPM, it probably didn't work for anyone else but me :/ It should be fixed now, hopefully it works for everyone. It does require Node v6 or greater.

  • Couldn't you automate detection of the Espruino so that the port doesn't have to be entered manually? Even just changing to a different USB port will cause this value to change. In the script that I wrote for this, I copied the code that the web IDE uses to identify Espruino boards and connect to the corresponding port:
    github.com/stokebrain/morra-buil­d/blob/master/main.js#L23

    Webpack also has tree-shaking. When I first tried rollup, I concluded it wasn't a good choice because I saw that it was placing top-level variables from all modules, all together in the same scope. I was too concerned about name collision and went back to webpack.

    Also, I don't see the smaller footprint that rollup is claiming.
    vkbansal.me/blog/rollup-first-im­pressions/

    I continually see people writing that rollup configuration is easier, but that has not been true for me. Webpack just worked, and rollup continues to require debugging.

    I still have some complaints and issues to solve with webpack. For my first project it all worked well, but in my current MQTT test, I'm getting errors when using my build-and-save script. One thing that concerns me is the use of arrays in the build, because I don't know if these will cause performance issues for Espruino.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Espruino plugin for Rollup module bundler

Posted by Avatar for Joakim @Joakim

Actions