more electron/web separation

This commit is contained in:
Eugene Pankov
2021-06-19 01:36:25 +02:00
parent fa31ac65ab
commit fad7858f3f
50 changed files with 568 additions and 448 deletions

View File

@@ -1,6 +1,8 @@
import { app, ipcMain, Menu, Tray, shell, screen, globalShortcut, MenuItemConstructorOptions } from 'electron'
import * as promiseIpc from 'electron-promise-ipc'
import * as remote from '@electron/remote/main'
import * as path from 'path'
import * as fs from 'fs'
import { loadConfig } from './config'
import { Window, WindowOptions } from './window'
@@ -17,6 +19,7 @@ export class Application {
private tray?: Tray
private ptyManager = new PTYManager()
private windows: Window[] = []
userPluginsPath: string
constructor () {
remote.initialize()
@@ -36,12 +39,12 @@ export class Application {
}
})
;(promiseIpc as any).on('plugin-manager:install', (path, name, version) => {
return pluginManager.install(path, name, version)
;(promiseIpc as any).on('plugin-manager:install', (name, version) => {
return pluginManager.install(this.userPluginsPath, name, version)
})
;(promiseIpc as any).on('plugin-manager:uninstall', (path, name) => {
return pluginManager.uninstall(path, name)
;(promiseIpc as any).on('plugin-manager:uninstall', (name) => {
return pluginManager.uninstall(this.userPluginsPath, name)
})
const configData = loadConfig()
@@ -53,6 +56,15 @@ export class Application {
}
}
this.userPluginsPath = path.join(
app.getPath('userData'),
'plugins',
)
if (!fs.existsSync(this.userPluginsPath)) {
fs.mkdirSync(this.userPluginsPath)
}
app.commandLine.appendSwitch('disable-http-cache')
app.commandLine.appendSwitch('max-active-webgl-contexts', '9000')
app.commandLine.appendSwitch('lang', 'EN')
@@ -70,7 +82,7 @@ export class Application {
}
async newWindow (options?: WindowOptions): Promise<Window> {
const window = new Window(options)
const window = new Window(this, options)
this.windows.push(window)
window.visible$.subscribe(visible => {
if (visible) {

View File

@@ -9,6 +9,7 @@ import * as path from 'path'
import macOSRelease from 'macos-release'
import * as compareVersions from 'compare-versions'
import type { Application } from './app'
import { parseArgs } from './cli'
import { loadConfig } from './config'
@@ -43,7 +44,7 @@ export class Window {
get visible$ (): Observable<boolean> { return this.visible }
get closed$ (): Observable<void> { return this.closed }
constructor (options?: WindowOptions) {
constructor (private application: Application, options?: WindowOptions) {
this.configStore = loadConfig()
options = options ?? {}
@@ -299,16 +300,10 @@ export class Window {
executable: app.getPath('exe'),
windowID: this.window.id,
isFirstWindow: this.window.id === 1,
userPluginsPath: this.application.userPluginsPath,
})
})
ipcMain.on('window-focus', event => {
if (!this.window || event.sender !== this.window.webContents) {
return
}
this.window.focus()
})
ipcMain.on('window-toggle-maximize', event => {
if (!this.window || event.sender !== this.window.webContents) {
return

View File

@@ -11,7 +11,7 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { ipcRenderer } from 'electron'
import { getRootModule } from './app.module'
import { findPlugins, loadPlugins, PluginInfo } from './plugins'
import { findPlugins, initModuleLookup, loadPlugins } from './plugins'
import { BootstrapData, BOOTSTRAP_DATA } from '../../terminus-core/src/api/mainProcess'
// Always land on the start view
@@ -29,12 +29,12 @@ if (process.env.TERMINUS_DEV && !process.env.TERMINUS_FORCE_ANGULAR_PROD) {
enableProdMode()
}
async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, safeMode = false): Promise<NgModuleRef<any>> {
async function bootstrap (bootstrapData: BootstrapData, safeMode = false): Promise<NgModuleRef<any>> {
if (safeMode) {
plugins = plugins.filter(x => x.isBuiltin)
bootstrapData.installedPlugins = bootstrapData.installedPlugins.filter(x => x.isBuiltin)
}
const pluginModules = await loadPlugins(plugins, (current, total) => {
const pluginModules = await loadPlugins(bootstrapData.installedPlugins, (current, total) => {
(document.querySelector('.progress .bar') as HTMLElement).style.width = `${100 * current / total}%` // eslint-disable-line
})
const module = getRootModule(pluginModules)
@@ -53,20 +53,24 @@ async function bootstrap (plugins: PluginInfo[], bootstrapData: BootstrapData, s
ipcRenderer.once('start', async (_$event, bootstrapData: BootstrapData) => {
console.log('Window bootstrap data:', bootstrapData)
initModuleLookup(bootstrapData.userPluginsPath)
let plugins = await findPlugins()
if (bootstrapData.config.pluginBlacklist) {
plugins = plugins.filter(x => !bootstrapData.config.pluginBlacklist.includes(x.name))
}
plugins = plugins.filter(x => x.name !== 'web')
bootstrapData.installedPlugins = plugins
console.log('Starting with plugins:', plugins)
try {
await bootstrap(plugins, bootstrapData)
await bootstrap(bootstrapData)
} catch (error) {
console.error('Angular bootstrapping error:', error)
console.warn('Trying safe mode')
window['safeModeReason'] = error
try {
await bootstrap(plugins, bootstrapData, true)
await bootstrap(bootstrapData, true)
} catch (error2) {
console.error('Bootstrap failed:', error2)
}

View File

@@ -1,8 +1,11 @@
import * as fs from 'mz/fs'
import * as path from 'path'
import * as remote from '@electron/remote'
import { PluginInfo } from '../../terminus-core/src/api/mainProcess'
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
const nodeRequire = (global as any).require
const nodeRequire = global['require']
function normalizePath (p: string): string {
const cygwinPrefix = '/cygdrive/'
@@ -13,45 +16,8 @@ function normalizePath (p: string): string {
return p
}
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
if (process.env.TERMINUS_DEV) {
nodeModule.globalPaths.unshift(path.dirname(remote.app.getAppPath()))
}
const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
const userPluginsPath = path.join(
remote.app.getPath('userData'),
'plugins',
)
if (!fs.existsSync(userPluginsPath)) {
fs.mkdir(userPluginsPath)
}
Object.assign(window, { builtinPluginsPath, userPluginsPath })
nodeModule.globalPaths.unshift(builtinPluginsPath)
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
if (process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
export interface PluginInfo {
name: string
description: string
packageName: string
isBuiltin: boolean
version: string
author: string
homepage?: string
path?: string
info?: any
}
const builtinModules = [
'@angular/animations',
'@angular/common',
@@ -71,25 +37,42 @@ const builtinModules = [
'zone.js/dist/zone.js',
]
const cachedBuiltinModules = {}
builtinModules.forEach(m => {
cachedBuiltinModules[m] = nodeRequire(m)
})
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
const originalRequire = (global as any).require
;(global as any).require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, [query])
}
export function initModuleLookup (userPluginsPath: string): void {
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
if (process.env.TERMINUS_DEV) {
nodeModule.globalPaths.unshift(path.dirname(remote.app.getAppPath()))
}
nodeModule.globalPaths.unshift(builtinPluginsPath)
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
if (process.env.TERMINUS_PLUGINS) {
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
}
const cachedBuiltinModules = {}
builtinModules.forEach(m => {
cachedBuiltinModules[m] = nodeRequire(m)
})
const originalRequire = (global as any).require
;(global as any).require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, [query])
}
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalModuleRequire.call(this, query)
}
return originalModuleRequire.call(this, query)
}
export async function findPlugins (): Promise<PluginInfo[]> {
@@ -167,8 +150,6 @@ export async function findPlugins (): Promise<PluginInfo[]> {
}
foundPlugins.sort((a, b) => a.name > b.name ? 1 : -1)
;(window as any).installedPlugins = foundPlugins
return foundPlugins
}