DEV Community

Johannes Zillmann
Johannes Zillmann

Posted on • Originally published at blog.morethan.io on

1 1

JS/TS Managing alternative implementations with RollupJS

Short post about a trivial thing to do. I’m in the JS/Electron world. I just decided I want to package my app for Electron but for a regular Browser as well. Why ?

  • A) I can do a demo version of the app on the web!
  • B) I can use Cypress for testing it!

Will see how far this goes, but currently i’m using only two Electron/Desktop features that can be easily mimicked in an browser environment:

  1. Reading and Writing app config => ElectronStore / Local Storage
  2. Reading and Writing files => Node FS API / Local Storage

Basic Structure

Simple. Lets just focus on the app config.

  • I defined a common ‘interface’ (AppConfig)
  • One implementation wrapping ElectronStore (ElectronAppConfig)
  • A second implementation wrapping the local storage (LocalAppConfig).

Most Naive Approach

I just kept all 3 classes under /src with a factory method:

export function createAppConfig(appConfigSchema) {
  if (__electronEnv__) {
    const ElectronStore = require('electron-store'); 
    return new ElelectronAppConfig(new ElectronStore({schema:appConfigSchem}));
  } else {
    const defaults = Object
      .keys(appConfigSchema)
      .reduce((o, key) => ({...o, [key]: appConfigSchema[key]['default'] }),{});
    return new LocalAppConfig(window.localStorage, defaults);
  }
}

Then in the rollup.config.js i’m using the plugin-replace to steer the __electronEnv__ variable:

import replace from '@rollup/plugin-replace';

const electronEnv = !!process.env.ELECTRON;

plugins: [
  replace({__electronEnv__: electronEnv}),
]

And finally i enrich my NPM electron tasks with then env variable in the package.json:

"electron": "ELECTRON=true run-s build pure-electron",

That’s it for the naive approach. It’s working most of the times (sometimes there is a hiccup with a require not found error, but a rebuild usually solves it).

Anyway, the purist in me, wanted a clearer structure and also the inline require statements seemed odd.

Moving to a more satisfactory approach

Have another folder next to /src, let’s called it /includes with three sub-folders:

  • api : AppConfig, …
  • electron : index.js (contain factory methods for all electron implementations), ElectronAppConfig, …
  • browser : index.js (contain factory methods for all browser implementations), LocalAppConfig, …

Now use plugin-alias to alias the index.js of the desired implementation at build time in rollup.config.js:

import alias from '@rollup/plugin-alias';
const electronEnv = !!process.env.ELECTRON;
const storagePackage = electronEnv ? 'electron' : 'browser';

plugins: [
  alias({
    entries: [
      { find: 'storage', replacement: `./includes/${storagePackage}/index.js` }
    ]
  })
]

And access the implementation in your main code:

import { createAppConfig } from 'storage';

const appConfig = createAppConfig(appConfigSchema);

Easy. Not too much gain here, but some clearer structure!

And now in Typescript…

Once i moved to the approach above, i thought ‘Ok, lets try typescript’. Cause that’s an obvious thing to do if you’re talking about interfaces and implementations, right ?

I failed using the exact same approach but luckily the typescript path-mapping came to rescue:

Here is the rollup.config.js part:

import typescript from '@rollup/plugin-typescript';

plugins: [
 typescript({ target: 'es6', baseUrl: './', paths: { storage: [`./includes/${storagePackage}/index.js`] } })
]

Imports work the same as in the previous approach!

Final Words

Not sure if i delivered on the promise of shortness, but finding the second/third approaches took me longer than expected and drove me almost nuts. Part i blame on my inexperience in the JS world, part is that the search space for such a problem seems heavily convoluted. That said, there might be a couple of alternatives worth investigating:

If you have any feedback or inspiration, let me know!


SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more