1
1
mirror of https://github.com/Eugeny/tabby.git synced 2025-01-04 08:32:55 +03:00
tabby/app/src/plugins.ts

188 lines
6.2 KiB
TypeScript
Raw Normal View History

2017-06-28 00:35:19 +03:00
import * as fs from 'mz/fs'
2017-04-15 16:20:18 +03:00
import * as path from 'path'
2019-06-15 00:47:48 +03:00
const nodeModule = require('module') // eslint-disable-line @typescript-eslint/no-var-requires
2017-05-01 14:35:26 +03:00
const nodeRequire = (global as any).require
2017-04-17 15:57:22 +03:00
function normalizePath (path: string): string {
const cygwinPrefix = '/cygdrive/'
if (path.startsWith(cygwinPrefix)) {
path = path.substring(cygwinPrefix.length).replace('/', '\\')
path = path[0] + ':' + path.substring(1)
}
return path
2017-05-01 14:35:26 +03:00
}
2017-04-17 15:57:22 +03:00
2019-09-09 17:23:47 +03:00
global['module'].paths.map((x: string) => nodeModule.globalPaths.push(normalizePath(x)))
2017-04-30 20:21:53 +03:00
2019-02-10 00:10:42 +03:00
if (process.env.TERMINUS_DEV) {
2017-04-30 20:21:53 +03:00
nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
}
2019-02-10 00:10:42 +03:00
const builtinPluginsPath = process.env.TERMINUS_DEV ? path.dirname(require('electron').remote.app.getAppPath()) : path.join((process as any).resourcesPath, 'builtin-plugins')
2017-06-01 23:23:36 +03:00
const userPluginsPath = path.join(
2017-04-30 20:21:53 +03:00
require('electron').remote.app.getPath('appData'),
'terminus',
'plugins',
2017-06-01 23:23:36 +03:00
)
if (!fs.existsSync(userPluginsPath)) {
fs.mkdir(userPluginsPath)
}
2017-06-01 23:23:36 +03:00
Object.assign(window, { builtinPluginsPath, userPluginsPath })
nodeModule.globalPaths.unshift(builtinPluginsPath)
2017-06-03 18:11:38 +03:00
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
2017-07-01 01:55:21 +03:00
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
2017-04-15 16:20:18 +03:00
if (process.env.TERMINUS_PLUGINS) {
2017-07-01 01:55:21 +03:00
process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
2017-04-15 16:20:18 +03:00
}
2019-06-15 00:47:48 +03:00
export type ProgressCallback = (current: number, total: number) => void // eslint-disable-line @typescript-eslint/no-type-alias
2017-04-24 01:34:07 +03:00
2019-06-15 00:47:48 +03:00
export interface PluginInfo {
2017-04-24 01:34:07 +03:00
name: string
2017-06-01 23:23:36 +03:00
description: string
packageName: string
isBuiltin: boolean
version: string
2017-07-05 16:31:23 +03:00
author: string
2017-06-01 23:23:36 +03:00
homepage?: string
path?: string
info?: any
2017-04-24 01:34:07 +03:00
}
2017-07-01 01:55:21 +03:00
const builtinModules = [
'@angular/animations',
'@angular/common',
'@angular/compiler',
'@angular/core',
'@angular/forms',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@ng-bootstrap/ng-bootstrap',
2018-01-19 17:31:28 +03:00
'ngx-toastr',
2017-07-01 01:55:21 +03:00
'rxjs',
2018-07-02 20:53:48 +03:00
'rxjs/operators',
2019-05-31 19:17:15 +03:00
'rxjs-compat/Subject',
2017-07-01 01:55:21 +03:00
'terminus-core',
'terminus-settings',
'terminus-terminal',
'zone.js/dist/zone.js',
]
2017-07-01 02:22:01 +03:00
const cachedBuiltinModules = {}
builtinModules.forEach(m => {
2019-05-31 19:17:15 +03:00
const label = 'Caching ' + m
console.time(label)
2017-07-01 02:22:01 +03:00
cachedBuiltinModules[m] = nodeRequire(m)
2019-05-31 19:17:15 +03:00
console.timeEnd(label)
2017-07-01 02:22:01 +03:00
})
2019-05-31 19:17:15 +03:00
const originalRequire = (global as any).require
2019-06-14 18:49:42 +03:00
;(global as any).require = function (query: string) {
2017-07-01 02:22:01 +03:00
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalRequire.apply(this, arguments)
}
const originalModuleRequire = nodeModule.prototype.require
nodeModule.prototype.require = function (query: string) {
if (cachedBuiltinModules[query]) {
return cachedBuiltinModules[query]
}
return originalModuleRequire.call(this, query)
}
2019-06-15 00:47:48 +03:00
export async function findPlugins (): Promise<PluginInfo[]> {
2019-06-14 18:49:42 +03:00
const paths = nodeModule.globalPaths
2019-06-15 00:47:48 +03:00
let foundPlugins: PluginInfo[] = []
2019-06-14 18:49:42 +03:00
const candidateLocations: { pluginDir: string, packageName: string }[] = []
const PREFIX = 'terminus-'
2017-04-24 01:34:07 +03:00
2017-04-15 16:20:18 +03:00
for (let pluginDir of paths) {
2017-04-17 15:57:22 +03:00
pluginDir = normalizePath(pluginDir)
2017-04-15 16:20:18 +03:00
if (!await fs.exists(pluginDir)) {
continue
}
2019-06-14 18:49:42 +03:00
const pluginNames = await fs.readdir(pluginDir)
2017-07-01 01:55:21 +03:00
if (await fs.exists(path.join(pluginDir, 'package.json'))) {
candidateLocations.push({
pluginDir: path.dirname(pluginDir),
2019-06-15 00:47:48 +03:00
packageName: path.basename(pluginDir),
2017-07-01 01:55:21 +03:00
})
}
2019-06-14 18:49:42 +03:00
for (const packageName of pluginNames) {
if (packageName.startsWith(PREFIX)) {
candidateLocations.push({ pluginDir, packageName })
}
2017-07-01 01:55:21 +03:00
}
}
2017-04-30 20:21:53 +03:00
2019-06-14 18:49:42 +03:00
for (const { pluginDir, packageName } of candidateLocations) {
const pluginPath = path.join(pluginDir, packageName)
const infoPath = path.join(pluginPath, 'package.json')
2017-07-01 01:55:21 +03:00
if (!await fs.exists(infoPath)) {
continue
}
2019-06-14 18:49:42 +03:00
const name = packageName.substring(PREFIX.length)
2017-11-27 00:14:46 +03:00
if (foundPlugins.some(x => x.name === name)) {
console.info(`Plugin ${packageName} already exists, overriding`)
foundPlugins = foundPlugins.filter(x => x.name !== name)
2017-07-01 01:55:21 +03:00
}
2017-04-30 20:21:53 +03:00
2017-07-01 01:55:21 +03:00
try {
2019-06-14 18:49:42 +03:00
const info = JSON.parse(await fs.readFile(infoPath, { encoding: 'utf-8' }))
2017-11-04 21:06:58 +03:00
if (!info.keywords || !(info.keywords.includes('terminus-plugin') || info.keywords.includes('terminus-builtin-plugin'))) {
2017-07-01 01:55:21 +03:00
continue
2017-04-24 22:26:59 +03:00
}
2017-07-05 16:31:23 +03:00
let author = info.author
author = author.name || author
2017-07-01 01:55:21 +03:00
foundPlugins.push({
2017-11-27 00:14:46 +03:00
name: name,
packageName: packageName,
2017-07-01 01:55:21 +03:00
isBuiltin: pluginDir === builtinPluginsPath,
version: info.version,
description: info.description,
2017-07-05 16:31:23 +03:00
author,
2017-07-01 01:55:21 +03:00
path: pluginPath,
info,
})
} catch (error) {
2017-11-27 00:14:46 +03:00
console.error('Cannot load package info for', packageName)
2017-04-24 22:26:59 +03:00
}
2017-04-15 16:20:18 +03:00
}
2017-04-24 22:26:59 +03:00
2017-06-01 23:23:36 +03:00
(window as any).installedPlugins = foundPlugins
2017-04-24 22:26:59 +03:00
return foundPlugins
}
2019-06-15 00:47:48 +03:00
export async function loadPlugins (foundPlugins: PluginInfo[], progress: ProgressCallback): Promise<any[]> {
2019-06-14 18:49:42 +03:00
const plugins: any[] = []
2017-04-24 22:26:59 +03:00
progress(0, 1)
2017-04-28 23:40:58 +03:00
let index = 0
2019-06-14 18:49:42 +03:00
for (const foundPlugin of foundPlugins) {
2017-05-01 14:35:26 +03:00
console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
2017-04-24 01:34:07 +03:00
progress(index, foundPlugins.length)
try {
2019-05-31 19:17:15 +03:00
const label = 'Loading ' + foundPlugin.name
console.time(label)
2019-06-14 18:49:42 +03:00
const packageModule = nodeRequire(foundPlugin.path)
const pluginModule = packageModule.default.forRoot ? packageModule.default.forRoot() : packageModule.default
2017-11-27 00:14:46 +03:00
pluginModule['pluginName'] = foundPlugin.name
pluginModule['bootstrap'] = packageModule.bootstrap
2017-04-24 01:34:07 +03:00
plugins.push(pluginModule)
2019-05-31 19:17:15 +03:00
console.timeEnd(label)
await new Promise(x => setTimeout(x, 50))
2017-04-24 01:34:07 +03:00
} catch (error) {
console.error(`Could not load ${foundPlugin.name}:`, error)
}
2017-04-28 23:40:58 +03:00
index++
}
2017-04-24 01:34:07 +03:00
progress(1, 1)
2017-04-15 16:20:18 +03:00
return plugins
}