Короткий пост о тривиальной вещи. Я в мире JS/Электрон. Я просто решил, что хочу упаковать свое приложение для Electron, но также и для обычного браузера. Почему ?

  • А) Я могу сделать демо-версию приложения в Интернете!
  • Б) Я могу использовать Кипарис для проверки!

Посмотрим, как далеко это зайдет, но в настоящее время я использую только две функции Electron/Desktop, которые можно легко имитировать в среде браузера:

  1. Чтение и запись конфигурации приложения => Электронный магазин / Локальное хранилище
  2. Чтение и запись файлов =› Node FS API / Локальное хранилище

Базовая структура

Простой. Давайте просто сосредоточимся на конфигурации приложения.

  • Я определил общий «интерфейс» (AppConfig)
  • Одна реализация-обертка ElectronStore (ElectronAppConfig)
  • Вторая реализация, обертывающая локальное хранилище (LocalAppConfig).

Самый наивный подход

Я просто сохранил все 3 класса под /src с помощью фабричного метода:

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);
  }
}

Затем в rollup.config.js я использую plugin-replace для управления переменной __electronEnv__:

import replace from '@rollup/plugin-replace';
const electronEnv = !!process.env.ELECTRON;
plugins: [
  replace({__electronEnv__: electronEnv}),
]

И, наконец, я обогащаю свои электронные задачи NPM с помощью переменной env в package.json:

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

Вот и все, что касается наивного подхода. В большинстве случаев он работает (иногда возникает сбой с ошибкой require not found, но обычно это решает перезагрузка).

Во всяком случае, пурист во мне хотел более четкой структуры, а встроенные require операторы казались странными.

Переход к более удовлетворительному подходу

Создайте еще одну папку рядом с /src, назовем ее /includes с тремя подпапками:

  • api: AppConfig, …
  • electron: index.js (содержит фабричные методы для всех электронных реализаций), ElectronAppConfig, …
  • браузер: index.js (содержит фабричные методы для всех реализаций браузера), LocalAppConfig, …

Теперь используйте plugin-alias для псевдонима index.js нужной реализации во время сборки в 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` }
    ]
  })
]

И получите доступ к реализации в вашем основном коде:

import { createAppConfig } from 'storage';
const appConfig = createAppConfig(appConfigSchema);

Легкий. Здесь не слишком много усиления, но какая-то более четкая структура…

А теперь в машинописном тексте…

Как только я перешел к описанному выше подходу, я подумал: «Хорошо, давайте попробуем машинопись». Потому что это очевидно, если вы говорите об интерфейсах и реализациях, верно?

Я потерпел неудачу, используя точно такой же подход, но, к счастью, на помощь пришел машинописный текст path-mapping:

Вот rollup.config.js часть:

import typescript from '@rollup/plugin-typescript';
plugins: [
 typescript({ target: 'es6', baseUrl: './', paths: { storage: [`./includes/${storagePackage}/index.js`] } })
]

Импорт работает так же, как и в предыдущем подходе!

Заключительные слова

Не уверен, что выполнил обещание о краткости, но поиск второго/третьего подходов занял у меня больше времени, чем ожидалось, и почти сводил меня с ума. Отчасти я виню свою неопытность в мире JS, отчасти то, что пространство для поиска такой проблемы кажется довольно запутанным. Тем не менее, может быть несколько альтернатив, которые стоит изучить:

Если у вас есть отзывы или вдохновение, дайте мне знать!