plugins.ts 4.9 KB
Newer Older
E
Eugene Pankov 已提交
1
import * as fs from 'mz/fs'
E
.  
Eugene Pankov 已提交
2
import * as path from 'path'
E
lint  
Eugene Pankov 已提交
3 4
const nodeModule = require('module')
const nodeRequire = (global as any).require
E
.  
Eugene Pankov 已提交
5

E
.  
Eugene Pankov 已提交
6 7
declare function delay (ms: number): Promise<void>

E
.  
Eugene Pankov 已提交
8 9 10 11 12 13 14
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
E
lint  
Eugene Pankov 已提交
15
}
E
.  
Eugene Pankov 已提交
16

E
lint  
Eugene Pankov 已提交
17
nodeRequire.main.paths.map(x => nodeModule.globalPaths.push(normalizePath(x)))
E
.  
Eugene Pankov 已提交
18 19 20 21 22

if (process.env.DEV) {
    nodeModule.globalPaths.unshift(path.dirname(require('electron').remote.app.getAppPath()))
}

E
wip  
Eugene Pankov 已提交
23
const builtinPluginsPath = path.join((process as any).resourcesPath, 'builtin-plugins')
E
wip  
Eugene Pankov 已提交
24 25

const userPluginsPath = path.join(
E
.  
Eugene Pankov 已提交
26 27 28
    require('electron').remote.app.getPath('appData'),
    'terminus',
    'plugins',
E
wip  
Eugene Pankov 已提交
29 30 31 32
)

Object.assign(window, { builtinPluginsPath, userPluginsPath })
nodeModule.globalPaths.unshift(builtinPluginsPath)
E
.  
Eugene Pankov 已提交
33
nodeModule.globalPaths.unshift(path.join(userPluginsPath, 'node_modules'))
E
wip  
Eugene Pankov 已提交
34
// nodeModule.globalPaths.unshift(path.join((process as any).resourcesPath, 'app.asar', 'node_modules'))
E
.  
Eugene Pankov 已提交
35
if (process.env.TERMINUS_PLUGINS) {
E
wip  
Eugene Pankov 已提交
36
    process.env.TERMINUS_PLUGINS.split(':').map(x => nodeModule.globalPaths.push(normalizePath(x)))
E
.  
Eugene Pankov 已提交
37 38
}

E
.  
Eugene Pankov 已提交
39 40
export declare type ProgressCallback = (current, total) => void

E
wip  
Eugene Pankov 已提交
41
export interface IPluginInfo {
E
.  
Eugene Pankov 已提交
42
    name: string
E
wip  
Eugene Pankov 已提交
43 44 45 46 47 48 49
    description: string
    packageName: string
    isBuiltin: boolean
    version: string
    homepage?: string
    path?: string
    info?: any
E
.  
Eugene Pankov 已提交
50 51
}

E
wip  
Eugene Pankov 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
const builtinModules = [
    '@angular/animations',
    '@angular/common',
    '@angular/compiler',
    '@angular/core',
    '@angular/forms',
    '@angular/platform-browser',
    '@angular/platform-browser-dynamic',
    '@ng-bootstrap/ng-bootstrap',
    'rxjs',
    'terminus-core',
    'terminus-settings',
    'terminus-terminal',
    'zone.js/dist/zone.js',
]

E
wip  
Eugene Pankov 已提交
68
export async function findPlugins (): Promise<IPluginInfo[]> {
E
.  
Eugene Pankov 已提交
69
    let paths = nodeModule.globalPaths
E
wip  
Eugene Pankov 已提交
70
    let foundPlugins: IPluginInfo[] = []
E
wip  
Eugene Pankov 已提交
71
    let candidateLocations: { pluginDir: string, pluginName: string }[] = []
E
.  
Eugene Pankov 已提交
72

E
.  
Eugene Pankov 已提交
73
    for (let pluginDir of paths) {
E
.  
Eugene Pankov 已提交
74
        pluginDir = normalizePath(pluginDir)
E
.  
Eugene Pankov 已提交
75 76 77
        if (!await fs.exists(pluginDir)) {
            continue
        }
E
.  
Eugene Pankov 已提交
78
        let pluginNames = await fs.readdir(pluginDir)
E
wip  
Eugene Pankov 已提交
79 80 81 82 83 84 85 86 87 88
        if (await fs.exists(path.join(pluginDir, 'package.json'))) {
            candidateLocations.push({
                pluginDir: path.dirname(pluginDir),
                pluginName: path.basename(pluginDir)
            })
        }
        for (let pluginName of pluginNames) {
            candidateLocations.push({ pluginDir, pluginName })
        }
    }
E
.  
Eugene Pankov 已提交
89

E
wip  
Eugene Pankov 已提交
90 91 92 93 94 95 96 97 98 99
    for (let { pluginDir, pluginName } of candidateLocations) {
        let pluginPath = path.join(pluginDir, pluginName)
        let infoPath = path.join(pluginPath, 'package.json')
        if (!await fs.exists(infoPath)) {
            continue
        }

        if (foundPlugins.some(x => x.name === pluginName)) {
            console.info(`Plugin ${pluginName} already exists`)
        }
E
.  
Eugene Pankov 已提交
100

E
wip  
Eugene Pankov 已提交
101 102 103 104
        try {
            let info = JSON.parse(await fs.readFile(infoPath, {encoding: 'utf-8'}))
            if (!info.keywords || info.keywords.indexOf('terminus-plugin') === -1) {
                continue
E
.  
Eugene Pankov 已提交
105
            }
E
wip  
Eugene Pankov 已提交
106 107 108 109 110 111 112 113 114 115 116
            foundPlugins.push({
                name: pluginName.substring('terminus-'.length),
                packageName: pluginName,
                isBuiltin: pluginDir === builtinPluginsPath,
                version: info.version,
                description: info.description,
                path: pluginPath,
                info,
            })
        } catch (error) {
            console.error('Cannot load package info for', pluginName)
E
.  
Eugene Pankov 已提交
117
        }
E
.  
Eugene Pankov 已提交
118
    }
E
.  
Eugene Pankov 已提交
119

E
wip  
Eugene Pankov 已提交
120
    (window as any).installedPlugins = foundPlugins
E
.  
Eugene Pankov 已提交
121 122 123
    return foundPlugins
}

E
wip  
Eugene Pankov 已提交
124
export async function loadPlugins (foundPlugins: IPluginInfo[], progress: ProgressCallback): Promise<any[]> {
E
.  
Eugene Pankov 已提交
125 126
    let plugins: any[] = []
    progress(0, 1)
E
.  
Eugene Pankov 已提交
127 128
    let index = 0
    for (let foundPlugin of foundPlugins) {
E
lint  
Eugene Pankov 已提交
129
        console.info(`Loading ${foundPlugin.name}: ${nodeRequire.resolve(foundPlugin.path)}`)
E
.  
Eugene Pankov 已提交
130
        progress(index, foundPlugins.length)
E
wip  
Eugene Pankov 已提交
131 132 133 134 135 136 137 138 139 140
        // pre-inject builtin modules
        builtinModules.forEach(moduleName => {
            let mod = nodeRequire(moduleName)
            let modPath = nodeRequire.resolve(moduleName)
            let modSubpath = modPath.substring(modPath.indexOf(moduleName))
            console.log('injecting', moduleName, modPath)
            let targetPath = path.join(foundPlugin.path, 'node_modules', modSubpath)
            console.log(targetPath, modPath)
            nodeRequire.cache[targetPath] = mod
        })
E
.  
Eugene Pankov 已提交
141
        try {
E
lint  
Eugene Pankov 已提交
142
            let pluginModule = nodeRequire(foundPlugin.path)
E
.  
Eugene Pankov 已提交
143 144 145 146
            plugins.push(pluginModule)
        } catch (error) {
            console.error(`Could not load ${foundPlugin.name}:`, error)
        }
E
.  
Eugene Pankov 已提交
147 148 149
        await delay(1)
        index++
    }
E
.  
Eugene Pankov 已提交
150
    progress(1, 1)
E
.  
Eugene Pankov 已提交
151 152
    return plugins
}