parent
c9297a4d8f
commit
f275f0ae8a
@ -0,0 +1,16 @@ |
||||
{ |
||||
"env": { |
||||
"browser": true, |
||||
"es6": true, |
||||
"node": true |
||||
}, |
||||
"extends": [ |
||||
"eslint:recommended", |
||||
"plugin:@typescript-eslint/eslint-recommended", |
||||
"plugin:@typescript-eslint/recommended", |
||||
"plugin:import/recommended", |
||||
"plugin:import/electron", |
||||
"plugin:import/typescript" |
||||
], |
||||
"parser": "@typescript-eslint/parser" |
||||
} |
@ -0,0 +1,92 @@ |
||||
# Logs |
||||
logs |
||||
*.log |
||||
npm-debug.log* |
||||
yarn-debug.log* |
||||
yarn-error.log* |
||||
lerna-debug.log* |
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html) |
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json |
||||
|
||||
# Runtime data |
||||
pids |
||||
*.pid |
||||
*.seed |
||||
*.pid.lock |
||||
.DS_Store |
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
lib-cov |
||||
|
||||
# Coverage directory used by tools like istanbul |
||||
coverage |
||||
*.lcov |
||||
|
||||
# nyc test coverage |
||||
.nyc_output |
||||
|
||||
# node-waf configuration |
||||
.lock-wscript |
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html) |
||||
build/Release |
||||
|
||||
# Dependency directories |
||||
node_modules/ |
||||
jspm_packages/ |
||||
|
||||
# TypeScript v1 declaration files |
||||
typings/ |
||||
|
||||
# TypeScript cache |
||||
*.tsbuildinfo |
||||
|
||||
# Optional npm cache directory |
||||
.npm |
||||
|
||||
# Optional eslint cache |
||||
.eslintcache |
||||
|
||||
# Optional REPL history |
||||
.node_repl_history |
||||
|
||||
# Output of 'npm pack' |
||||
*.tgz |
||||
|
||||
# Yarn Integrity file |
||||
.yarn-integrity |
||||
|
||||
# dotenv environment variables file |
||||
.env |
||||
.env.test |
||||
|
||||
# parcel-bundler cache (https://parceljs.org/) |
||||
.cache |
||||
|
||||
# next.js build output |
||||
.next |
||||
|
||||
# nuxt.js build output |
||||
.nuxt |
||||
|
||||
# vuepress build output |
||||
.vuepress/dist |
||||
|
||||
# Serverless directories |
||||
.serverless/ |
||||
|
||||
# FuseBox cache |
||||
.fusebox/ |
||||
|
||||
# DynamoDB Local files |
||||
.dynamodb/ |
||||
|
||||
# Webpack |
||||
.webpack/ |
||||
|
||||
# Vite |
||||
.vite/ |
||||
|
||||
# Electron-Forge |
||||
out/ |
@ -0,0 +1,35 @@ |
||||
import type { ForgeConfig } from '@electron-forge/shared-types'; |
||||
import { MakerSquirrel } from '@electron-forge/maker-squirrel'; |
||||
import { MakerZIP } from '@electron-forge/maker-zip'; |
||||
import { MakerDeb } from '@electron-forge/maker-deb'; |
||||
import { MakerRpm } from '@electron-forge/maker-rpm'; |
||||
import { WebpackPlugin } from '@electron-forge/plugin-webpack'; |
||||
|
||||
import { mainConfig } from './webpack.main.config'; |
||||
import { rendererConfig } from './webpack.renderer.config'; |
||||
|
||||
const config: ForgeConfig = { |
||||
packagerConfig: {}, |
||||
rebuildConfig: {}, |
||||
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})], |
||||
plugins: [ |
||||
new WebpackPlugin({ |
||||
mainConfig, |
||||
renderer: { |
||||
config: rendererConfig, |
||||
entryPoints: [ |
||||
{ |
||||
html: './src/index.html', |
||||
js: './src/renderer.ts', |
||||
name: 'main_window', |
||||
preload: { |
||||
js: './src/preload.ts', |
||||
}, |
||||
}, |
||||
], |
||||
}, |
||||
}), |
||||
], |
||||
}; |
||||
|
||||
export default config; |
@ -0,0 +1,44 @@ |
||||
{ |
||||
"name": "1test", |
||||
"productName": "1test", |
||||
"version": "1.0.0", |
||||
"description": "My Electron application description", |
||||
"main": ".webpack/main", |
||||
"scripts": { |
||||
"start": "electron-forge start", |
||||
"package": "electron-forge package", |
||||
"make": "electron-forge make", |
||||
"publish": "electron-forge publish", |
||||
"lint": "eslint --ext .ts,.tsx ." |
||||
}, |
||||
"keywords": [], |
||||
"author": { |
||||
"name": "filip mertens", |
||||
"email": "filip.mertens@ethereum.org" |
||||
}, |
||||
"license": "MIT", |
||||
"devDependencies": { |
||||
"@electron-forge/cli": "^6.1.1", |
||||
"@electron-forge/maker-deb": "^6.1.1", |
||||
"@electron-forge/maker-rpm": "^6.1.1", |
||||
"@electron-forge/maker-squirrel": "^6.1.1", |
||||
"@electron-forge/maker-zip": "^6.1.1", |
||||
"@electron-forge/plugin-webpack": "^6.1.1", |
||||
"@typescript-eslint/eslint-plugin": "^5.0.0", |
||||
"@typescript-eslint/parser": "^5.0.0", |
||||
"@vercel/webpack-asset-relocator-loader": "1.7.3", |
||||
"css-loader": "^6.0.0", |
||||
"electron": "25.0.0", |
||||
"eslint": "^8.0.1", |
||||
"eslint-plugin-import": "^2.25.0", |
||||
"fork-ts-checker-webpack-plugin": "^7.2.13", |
||||
"node-loader": "^2.0.0", |
||||
"style-loader": "^3.0.0", |
||||
"ts-loader": "^9.2.2", |
||||
"ts-node": "^10.0.0", |
||||
"typescript": "~4.5.4" |
||||
}, |
||||
"dependencies": { |
||||
"electron-squirrel-startup": "^1.0.0" |
||||
} |
||||
} |
@ -0,0 +1,38 @@ |
||||
import { ClientConnector, connectClient, applyApi, Client, PluginClient } from '@remixproject/plugin' |
||||
import type { Message, Api, ApiMap } from '@remixproject/plugin-utils' |
||||
import { IRemixApi } from '@remixproject/plugin-api' |
||||
import { ipcMain } from 'electron' |
||||
import { mainWindow } from '..' |
||||
|
||||
export class ElectronPluginClientConnector implements ClientConnector { |
||||
|
||||
constructor(public IPCName: string) {
|
||||
console.log('ElectronPluginClientConnector constructor', IPCName) |
||||
} |
||||
|
||||
|
||||
/** Send a message to the engine */ |
||||
send(message: Partial<Message>) { |
||||
console.log('ElectronPluginConnector send', message) |
||||
mainWindow.webContents.send(this.IPCName + ':send', message) |
||||
} |
||||
|
||||
/** Listen to message from the engine */ |
||||
on(cb: (message: Partial<Message>) => void) { |
||||
console.log('ElectronPluginConnector on', cb) |
||||
ipcMain.on(this.IPCName + ':on', (event, message) => { |
||||
console.log('ElectronPluginConnector on message received', message) |
||||
cb(message) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
export const createClient = < |
||||
P extends Api, |
||||
App extends ApiMap = Readonly<IRemixApi> |
||||
>(client: PluginClient<P, App> = new PluginClient(), IPCName: string): Client<P, App> => { |
||||
const c = client as any |
||||
connectClient(new ElectronPluginClientConnector(IPCName), c) |
||||
applyApi(c) |
||||
return c |
||||
} |
@ -0,0 +1,18 @@ |
||||
import { Engine, PluginManager, Plugin } from '@remixproject/engine'; |
||||
import { ipcMain, ipcRenderer } from 'electron'; |
||||
import { FSPlugin } from './fsPlugin'; |
||||
import { GitPlugin } from './gitPlugin'; |
||||
|
||||
const engine = new Engine() |
||||
const appManager = new PluginManager() |
||||
const fsPlugin = new FSPlugin() |
||||
const gitPlugin = new GitPlugin() |
||||
engine.register(appManager) |
||||
engine.register(fsPlugin) |
||||
engine.register(gitPlugin) |
||||
//appManager.activatePlugin('fs')
|
||||
|
||||
ipcMain.on('engine:activatePlugin', (event, arg) => { |
||||
console.log('engine:activatePlugin', arg) |
||||
appManager.activatePlugin(arg) |
||||
}) |
@ -0,0 +1,72 @@ |
||||
import { PluginClient } from "@remixproject/plugin"; |
||||
import { createClient } from "./electronPluginClient" |
||||
import { Engine, PluginManager, Plugin } from '@remixproject/engine'; |
||||
import fs from 'fs' |
||||
import { existsSync } from "fs-extra"; |
||||
|
||||
const profile = { |
||||
displayName: 'fs', |
||||
name: 'fs', |
||||
description: 'fs', |
||||
} |
||||
|
||||
export class FSPlugin extends Plugin { |
||||
client: PluginClient |
||||
constructor(){ |
||||
super(profile) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
console.log('fsPlugin onActivation') |
||||
this.client = new FSPluginClient() |
||||
} |
||||
} |
||||
|
||||
class FSPluginClient extends PluginClient { |
||||
constructor(){ |
||||
super() |
||||
this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists'] |
||||
createClient(this, 'fs') |
||||
this.onload(() => { |
||||
console.log('fsPluginClient onload') |
||||
}) |
||||
} |
||||
|
||||
async readdir(path: string): Promise<string[]> { |
||||
// call node fs.readdir
|
||||
return fs.readdirSync(path) |
||||
} |
||||
|
||||
async readFile(path: string): Promise<string> { |
||||
return fs.readFileSync(path, 'utf8') |
||||
} |
||||
|
||||
async writeFile(path: string, content: string): Promise<void> { |
||||
return fs.writeFileSync(path, content, 'utf8') |
||||
} |
||||
|
||||
async mkdir(path: string): Promise<void> { |
||||
return fs.mkdirSync(path) |
||||
} |
||||
|
||||
async rmdir(path: string): Promise<void> { |
||||
return fs.rmdirSync(path) |
||||
} |
||||
|
||||
async unlink(path: string): Promise<void> { |
||||
return fs.unlinkSync(path) |
||||
} |
||||
|
||||
async rename(oldPath: string, newPath: string): Promise<void> { |
||||
return fs.renameSync(oldPath, newPath) |
||||
} |
||||
|
||||
async stat(path: string): Promise<fs.Stats> { |
||||
return fs.statSync(path) |
||||
} |
||||
|
||||
async exists(path: string): Promise<boolean> { |
||||
return existsSync(path) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,46 @@ |
||||
import { Plugin } from "@remixproject/engine"; |
||||
import { PluginClient } from "@remixproject/plugin"; |
||||
import { Profile } from "@remixproject/plugin-utils"; |
||||
import { spawn } from "child_process"; |
||||
import { createClient } from "./electronPluginClient"; |
||||
|
||||
const profile: Profile = { |
||||
name: 'git', |
||||
displayName: 'Git', |
||||
description: 'Git plugin', |
||||
} |
||||
|
||||
export class GitPlugin extends Plugin { |
||||
client: PluginClient |
||||
constructor() { |
||||
super(profile) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
console.log('GitPlugin onActivation') |
||||
this.client = new GitPluginClient() |
||||
} |
||||
} |
||||
|
||||
class GitPluginClient extends PluginClient { |
||||
constructor() { |
||||
super() |
||||
this.methods = ['log', 'status', 'add', 'commit', 'push', 'pull', 'clone', 'checkout', 'branch', 'merge', 'reset', 'revert', 'diff', 'stash', 'apply', 'cherryPick', 'rebase', 'tag', 'fetch', 'remote', 'config', 'show', 'init', 'help', 'version'] |
||||
createClient(this, 'git') |
||||
this.onload(() => { |
||||
console.log('GitPluginClient onload') |
||||
}) |
||||
} |
||||
|
||||
async log(path: string): Promise<string> { |
||||
const log = spawn('git', ['log'], { cwd: path }) |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
log.stdout.on('data', (data) => { |
||||
resolve(data.toString()) |
||||
}) |
||||
}) |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,13 @@ |
||||
export interface IElectronAPI { |
||||
activatePlugin: (name: string) => void |
||||
receiveFromFS: (cb: any) => void |
||||
sendToFS: (message: Partial<Message>) => void |
||||
receiveFromGit: (cb: any) => void |
||||
sendToGit: (message: Partial<Message>) => void |
||||
} |
||||
|
||||
declare global { |
||||
interface Window { |
||||
api: IElectronAPI |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
body { |
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, |
||||
Arial, sans-serif; |
||||
margin: auto; |
||||
max-width: 38rem; |
||||
padding: 2rem; |
||||
} |
@ -0,0 +1,12 @@ |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="UTF-8" /> |
||||
<title>Hello World!</title> |
||||
|
||||
</head> |
||||
<body> |
||||
<h1>💖 Hello World!</h1> |
||||
<p>Welcome to your Electron application.</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,58 @@ |
||||
import { app, BrowserWindow } from 'electron'; |
||||
import path from 'path'; |
||||
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
|
||||
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
|
||||
// whether you're running in development or production).
|
||||
declare const MAIN_WINDOW_WEBPACK_ENTRY: string; |
||||
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; |
||||
|
||||
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
|
||||
if (require('electron-squirrel-startup')) { |
||||
app.quit(); |
||||
} |
||||
export let mainWindow: BrowserWindow; |
||||
const createWindow = (): void => { |
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({ |
||||
height: 800, |
||||
width: 600, |
||||
webPreferences: { |
||||
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY |
||||
}, |
||||
}); |
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); |
||||
|
||||
//mainWindow.maximize();
|
||||
// Open the DevTools.
|
||||
mainWindow.webContents.openDevTools(); |
||||
require('./electron/engine') |
||||
}; |
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow); |
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on('window-all-closed', () => { |
||||
if (process.platform !== 'darwin') { |
||||
app.quit(); |
||||
} |
||||
}); |
||||
|
||||
app.on('activate', () => { |
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) { |
||||
createWindow(); |
||||
} |
||||
}); |
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and import them here.
|
||||
|
||||
|
@ -0,0 +1,16 @@ |
||||
|
||||
import { Message } from '@remixproject/plugin-utils' |
||||
import { contextBridge, ipcRenderer } from 'electron' |
||||
|
||||
console.log('preload.ts') |
||||
|
||||
contextBridge.exposeInMainWorld('api', { |
||||
activatePlugin: (name: string) => { |
||||
console.log('activatePlugin', name) |
||||
ipcRenderer.send('engine:activatePlugin', name) |
||||
}, |
||||
receiveFromFS: (cb:any) => ipcRenderer.on('fs:send', cb), |
||||
sendToFS: (message: Partial<Message>) => ipcRenderer.send('fs:on', message), |
||||
receiveFromGit: (cb:any) => ipcRenderer.on('git:send', cb), |
||||
sendToGit: (message: Partial<Message>) => ipcRenderer.send('git:on', message) |
||||
}) |
@ -0,0 +1,157 @@ |
||||
import type { ExternalProfile, Profile, Message, PluginOptions } from '@remixproject/plugin-utils' |
||||
import { Plugin } from '@remixproject/engine'; |
||||
|
||||
|
||||
export interface PluginConnectorOptions extends PluginOptions { |
||||
engine?:string |
||||
} |
||||
|
||||
|
||||
export abstract class ElectronPluginConnector extends Plugin { |
||||
protected loaded: boolean |
||||
protected id = 0 |
||||
protected pendingRequest: Record<number, (result: any, error: Error | string) => void> = {} |
||||
protected options: PluginConnectorOptions |
||||
profile: Profile |
||||
constructor(profile: Profile) { |
||||
super(profile) |
||||
} |
||||
|
||||
/** |
||||
* Send a message to the external plugin |
||||
* @param message the message passed to the plugin |
||||
*/ |
||||
protected abstract send(message: Partial<Message>): void |
||||
/** |
||||
* Open connection with the plugin |
||||
* @param url The transformed url the plugin should connect to |
||||
*/ |
||||
protected abstract connect(name: string): any | Promise<any> |
||||
/** Close connection with the plugin */ |
||||
protected abstract disconnect(): any | Promise<any> |
||||
|
||||
async activate() { |
||||
await this.connect(this.profile.name) |
||||
return super.activate() |
||||
} |
||||
|
||||
async deactivate() { |
||||
this.loaded = false |
||||
await this.disconnect() |
||||
return super.deactivate() |
||||
} |
||||
|
||||
/** Set options for an external plugin */ |
||||
setOptions(options: Partial<PluginConnectorOptions> = {}) { |
||||
super.setOptions(options) |
||||
} |
||||
|
||||
/** Call a method from this plugin */ |
||||
protected callPluginMethod(key: string, payload: any[] = []): Promise<any> { |
||||
const action = 'request' |
||||
const id = this.id++ |
||||
const requestInfo = this.currentRequest |
||||
const name = this.name |
||||
const promise = new Promise((res, rej) => { |
||||
this.pendingRequest[id] = (result: any[], error: Error | string) => error ? rej (error) : res(result) |
||||
}) |
||||
this.send({ id, action, key, payload, requestInfo, name }) |
||||
return promise |
||||
} |
||||
|
||||
/** Perform handshake with the client if not loaded yet */ |
||||
protected async handshake() { |
||||
console.log('ElectronPluginConnector handshake', this.loaded) |
||||
if (!this.loaded) { |
||||
this.loaded = true |
||||
let methods: string[]; |
||||
try { |
||||
console.log('ElectronPluginConnector handshake calling plugin method') |
||||
methods = await this.callPluginMethod('handshake', [this.profile.name, this.options?.engine]) |
||||
console.log('ElectronPluginConnector handshake methods', methods) |
||||
} catch (err) { |
||||
console.error('ElectronPluginConnector handshake error', err) |
||||
this.loaded = false |
||||
throw err; |
||||
} |
||||
if (methods) { |
||||
this.profile.methods = methods |
||||
this.call('manager', 'updateProfile', this.profile) |
||||
} |
||||
} else { |
||||
// If there is a broken connection we want send back the handshake to the plugin client
|
||||
console.log('ElectronPluginConnector handshake already loaded') |
||||
return this.callPluginMethod('handshake', [this.profile.name, this.options?.engine]) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* React when a message comes from client |
||||
* @param message The message sent by the client |
||||
*/ |
||||
protected async getMessage(message: Message) { |
||||
console.log('ElectronPluginConnector getMessage', message) |
||||
// Check for handshake request from the client
|
||||
if (message.action === 'request' && message.key === 'handshake') { |
||||
return this.handshake() |
||||
} |
||||
|
||||
switch (message.action) { |
||||
// Start listening on an event
|
||||
case 'on': |
||||
case 'listen': { |
||||
const { name, key } = message |
||||
const action = 'notification' |
||||
this.on(name, key, (...payload: any[]) => this.send({ action, name, key, payload })) |
||||
break |
||||
} |
||||
case 'off': { |
||||
const { name, key } = message |
||||
this.off(name, key) |
||||
break |
||||
} |
||||
case 'once': { |
||||
const { name, key } = message |
||||
const action = 'notification' |
||||
this.once(name, key, (...payload: any) => this.send({ action, name, key, payload })) |
||||
break |
||||
} |
||||
// Emit an event
|
||||
case 'emit': |
||||
case 'notification': { |
||||
if (!message.payload) break |
||||
this.emit(message.key, ...message.payload) |
||||
break |
||||
} |
||||
// Call a method
|
||||
case 'call': |
||||
case 'request': { |
||||
const action = 'response' |
||||
try { |
||||
const payload = await this.call(message.name, message.key, ...message.payload) |
||||
const error: any = undefined |
||||
this.send({ ...message, action, payload, error }) |
||||
} catch (err) { |
||||
const payload: any = undefined |
||||
const error = err.message || err |
||||
this.send({ ...message, action, payload, error }) |
||||
} |
||||
break |
||||
} |
||||
case 'cancel': { |
||||
const payload = this.cancel(message.name, message.key) |
||||
break; |
||||
} |
||||
// Return result from exposed method
|
||||
case 'response': { |
||||
const { id, payload, error } = message |
||||
this.pendingRequest[id](payload, error) |
||||
delete this.pendingRequest[id] |
||||
break |
||||
} |
||||
default: { |
||||
throw new Error('Message should be a notification, request or response') |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
import { Engine, PluginManager, Plugin, PluginConnector } from '@remixproject/engine'; |
||||
import { Message, Profile } from '@remixproject/plugin-utils'; |
||||
import { ElectronPluginConnector } from './electronPluginConnector'; |
||||
|
||||
export class fsPlugin extends ElectronPluginConnector { |
||||
constructor(){ |
||||
super({ |
||||
displayName: 'fs', |
||||
name: 'fs', |
||||
description: 'fs', |
||||
}) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
console.log('fsPlugin onActivation') |
||||
//window.api.activatePlugin('fs')
|
||||
} |
||||
|
||||
protected connect(name: string) { |
||||
console.log('fsPlugin connect', name) |
||||
window.api.activatePlugin(name) |
||||
window.api.receiveFromFS((event: any, message: any) => { |
||||
console.log('fsPlugin fsClientSend message received', message) |
||||
this.getMessage(message) |
||||
}) |
||||
} |
||||
|
||||
protected send(message: Partial<Message>): void { |
||||
console.log('fsPlugin send', message) |
||||
window.api.sendToFS(message) |
||||
} |
||||
|
||||
protected disconnect() { |
||||
console.log('fsPlugin disconnect') |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
import { Engine, PluginManager, Plugin, PluginConnector } from '@remixproject/engine'; |
||||
import { Message, Profile } from '@remixproject/plugin-utils'; |
||||
import { ElectronPluginConnector } from './electronPluginConnector'; |
||||
|
||||
export class gitPlugin extends ElectronPluginConnector { |
||||
constructor(){ |
||||
super({ |
||||
displayName: 'git', |
||||
name: 'git', |
||||
description: 'git', |
||||
}) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
console.log('git onActivation') |
||||
//window.api.activatePlugin('fs')
|
||||
} |
||||
|
||||
protected connect(name: string) { |
||||
console.log('git connect', name) |
||||
window.api.activatePlugin(name) |
||||
window.api.receiveFromGit((event: any, message: any) => { |
||||
console.log('git fsClientSend message received', message) |
||||
this.getMessage(message) |
||||
}) |
||||
} |
||||
|
||||
protected send(message: Partial<Message>): void { |
||||
console.log('git send', message) |
||||
window.api.sendToGit(message) |
||||
} |
||||
|
||||
protected disconnect() { |
||||
console.log('git disconnect') |
||||
} |
||||
} |
@ -0,0 +1,25 @@ |
||||
|
||||
import { Engine, PluginManager, Plugin, PluginConnector } from '@remixproject/engine'; |
||||
import { Profile } from '@remixproject/plugin-utils'; |
||||
import { fsPlugin } from './remix/lib/fsPlugin'; |
||||
import { gitPlugin } from './remix/lib/gitPlugin'; |
||||
|
||||
|
||||
|
||||
const engine = new Engine() |
||||
const appManager = new PluginManager() |
||||
const fs = new fsPlugin() |
||||
const git = new gitPlugin() |
||||
engine.register(appManager) |
||||
engine.register(fs) |
||||
engine.register(git) |
||||
//appManager.activatePlugin('fs')
|
||||
appManager.activatePlugin('git') |
||||
|
||||
setTimeout(async () => { |
||||
//const files = await appManager.call('fs', 'readdir', './')
|
||||
//console.log('files', files)
|
||||
const log = await appManager.call('git', 'log', './') |
||||
console.log('log', log) |
||||
}, 5000) |
||||
|
@ -0,0 +1,19 @@ |
||||
{ |
||||
"compilerOptions": { |
||||
"target": "ES6", |
||||
"allowJs": true, |
||||
"module": "commonjs", |
||||
"skipLibCheck": true, |
||||
"esModuleInterop": true, |
||||
"noImplicitAny": true, |
||||
"sourceMap": true, |
||||
"baseUrl": ".", |
||||
"outDir": "dist", |
||||
"moduleResolution": "node", |
||||
"resolveJsonModule": true, |
||||
"paths": { |
||||
"*": ["node_modules/*"] |
||||
} |
||||
}, |
||||
"include": ["src/**/*"] |
||||
} |
@ -0,0 +1,18 @@ |
||||
import type { Configuration } from 'webpack'; |
||||
|
||||
import { rules } from './webpack.rules'; |
||||
|
||||
export const mainConfig: Configuration = { |
||||
/** |
||||
* This is the main entry point for your application, it's the first file |
||||
* that runs in the main process. |
||||
*/ |
||||
entry: './src/index.ts', |
||||
// Put your normal webpack config below here
|
||||
module: { |
||||
rules, |
||||
}, |
||||
resolve: { |
||||
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'], |
||||
}, |
||||
}; |
@ -0,0 +1,10 @@ |
||||
import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; |
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); |
||||
|
||||
export const plugins = [ |
||||
new ForkTsCheckerWebpackPlugin({ |
||||
logger: 'webpack-infrastructure', |
||||
}), |
||||
]; |
@ -0,0 +1,19 @@ |
||||
import type { Configuration } from 'webpack'; |
||||
|
||||
import { rules } from './webpack.rules'; |
||||
import { plugins } from './webpack.plugins'; |
||||
|
||||
rules.push({ |
||||
test: /\.css$/, |
||||
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], |
||||
}); |
||||
|
||||
export const rendererConfig: Configuration = { |
||||
module: { |
||||
rules, |
||||
}, |
||||
plugins, |
||||
resolve: { |
||||
extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], |
||||
}, |
||||
}; |
@ -0,0 +1,31 @@ |
||||
import type { ModuleOptions } from 'webpack'; |
||||
|
||||
export const rules: Required<ModuleOptions>['rules'] = [ |
||||
// Add support for native node modules
|
||||
{ |
||||
// We're specifying native_modules in the test because the asset relocator loader generates a
|
||||
// "fake" .node file which is really a cjs file.
|
||||
test: /native_modules[/\\].+\.node$/, |
||||
use: 'node-loader', |
||||
}, |
||||
{ |
||||
test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, |
||||
parser: { amd: false }, |
||||
use: { |
||||
loader: '@vercel/webpack-asset-relocator-loader', |
||||
options: { |
||||
outputAssetBase: 'native_modules', |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
test: /\.tsx?$/, |
||||
exclude: /(node_modules|\.webpack)/, |
||||
use: { |
||||
loader: 'ts-loader', |
||||
options: { |
||||
transpileOnly: true, |
||||
}, |
||||
}, |
||||
}, |
||||
]; |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@ |
||||
import { Engine, PluginManager, Plugin } from '@remixproject/engine'; |
||||
import { ipcMain, ipcRenderer } from 'electron'; |
||||
import { fsPlugin } from './fsPlugin'; |
||||
|
||||
const engine = new Engine() |
||||
const appManager = new PluginManager() |
||||
const plugin = new fsPlugin() |
||||
engine.register(appManager) |
||||
engine.register(plugin) |
||||
//appManager.activatePlugin('fs')
|
||||
|
||||
ipcMain.on('engine:activatePlugin', (event, arg) => { |
||||
appManager.activatePlugin(arg) |
||||
}) |
@ -0,0 +1,70 @@ |
||||
import { PluginClient } from "@remixproject/plugin"; |
||||
import { createClient } from "./electronPluginClient" |
||||
import { Engine, PluginManager, Plugin } from '@remixproject/engine'; |
||||
import fs from 'fs' |
||||
import { existsSync } from "fs-extra"; |
||||
|
||||
const profile = { |
||||
displayName: 'fs', |
||||
name: 'fs', |
||||
description: 'fs', |
||||
} |
||||
|
||||
export class fsPlugin extends Plugin { |
||||
client: PluginClient |
||||
constructor(){ |
||||
super(profile) |
||||
} |
||||
|
||||
onActivation(): void { |
||||
console.log('fsPlugin onActivation') |
||||
this.client = new fsPluginClient() |
||||
} |
||||
} |
||||
|
||||
class fsPluginClient extends PluginClient { |
||||
constructor(){ |
||||
super() |
||||
this.methods = ['readdir', 'readFile', 'writeFile', 'mkdir', 'rmdir', 'unlink', 'rename', 'stat', 'exists'] |
||||
createClient(this) |
||||
this.onload() |
||||
} |
||||
|
||||
async readdir(path: string): Promise<string[]> { |
||||
// call node fs.readdir
|
||||
return fs.readdirSync(path) |
||||
} |
||||
|
||||
async readFile(path: string): Promise<string> { |
||||
return fs.readFileSync(path, 'utf8') |
||||
} |
||||
|
||||
async writeFile(path: string, content: string): Promise<void> { |
||||
return fs.writeFileSync(path, content, 'utf8') |
||||
} |
||||
|
||||
async mkdir(path: string): Promise<void> { |
||||
return fs.mkdirSync(path) |
||||
} |
||||
|
||||
async rmdir(path: string): Promise<void> { |
||||
return fs.rmdirSync(path) |
||||
} |
||||
|
||||
async unlink(path: string): Promise<void> { |
||||
return fs.unlinkSync(path) |
||||
} |
||||
|
||||
async rename(oldPath: string, newPath: string): Promise<void> { |
||||
return fs.renameSync(oldPath, newPath) |
||||
} |
||||
|
||||
async stat(path: string): Promise<fs.Stats> { |
||||
return fs.statSync(path) |
||||
} |
||||
|
||||
async exists(path: string): Promise<boolean> { |
||||
return existsSync(path) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,12 @@ |
||||
|
||||
import { Message } from '@remixproject/plugin-utils' |
||||
import { contextBridge, ipcRenderer } from 'electron' |
||||
|
||||
console.log('preload.ts') |
||||
|
||||
contextBridge.exposeInMainWorld('api', { |
||||
activatePlugin: (name: string) => { |
||||
console.log('activatePlugin', name) |
||||
ipcRenderer.send('engine:activatePlugin', name) |
||||
} |
||||
}) |
Loading…
Reference in new issue