improve(plugin): basic impls of file storage api for specific plugin context

This commit is contained in:
charlie
2021-07-09 15:19:13 +08:00
committed by Tienson Qin
parent e2a047c1ac
commit 661bb064f7
12 changed files with 234 additions and 17 deletions

View File

@@ -220,7 +220,11 @@ class LSPluginCaller extends EventEmitter {
this._call = async (...args: any) => {
// parent all will get message before handshaked
await refChild.call(LSPMSGFn(pl.id), { type: args[0], payload: args[1] || {} })
await refChild.call(LSPMSGFn(pl.id), {
type: args[0], payload: Object.assign(args[1] || {}, {
$$pid: pl.id
})
})
}
this._callUserModel = async (type, payload: any) => {
@@ -263,7 +267,9 @@ class LSPluginCaller extends EventEmitter {
// TODO: support sync call
// @ts-ignore Call in same thread
this._pluginLocal?.emit(type, payload)
this._pluginLocal?.emit(type, Object.assign(payload, {
$$pid: pl.id
}))
return actor?.promise
}

View File

@@ -7,7 +7,7 @@ import {
setupInjectedUI,
deferred,
invokeHostExportedApi,
isObject, withFileProtocol
isObject, withFileProtocol, IS_DEV, getSDKPathRoot
} from './helpers'
import * as pluginHelpers from './helpers'
import Debug from 'debug'
@@ -477,7 +477,7 @@ class PluginLocal
if (!entry.endsWith('.js')) return
let sdkPath = await invokeHostExportedApi('_callApplication', 'getAppPath')
let sdkPathRoot = await getSDKPathRoot()
let entryPath = await invokeHostExportedApi(
'write_user_tmp_file',
`${this._id}_index.html`,
@@ -486,7 +486,7 @@ class PluginLocal
<head>
<meta charset="UTF-8">
<title>logseq plugin entry</title>
<script src="${sdkPath}/js/lsplugin.user.js"></script>
<script src="${sdkPathRoot}/lsplugin.user.js"></script>
</head>
<body>
<div id="app"></div>

View File

@@ -1,6 +1,7 @@
import EventEmitter from 'eventemitter3'
import * as CSS from 'csstype'
import { LSPluginCaller } from './LSPlugin.caller'
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
export type PluginLocalIdentity = string
@@ -494,4 +495,6 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
App: IAppProxy & Record<string, any>
Editor: IEditorProxy & Record<string, any>
DB: IDBProxy
FileStorage: LSPluginFileStorage
}

View File

@@ -16,6 +16,7 @@ import Debug from 'debug'
import * as CSS from 'csstype'
import { snakeCase } from 'snake-case'
import EventEmitter from 'eventemitter3'
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
declare global {
interface Window {
@@ -154,7 +155,7 @@ const editor: Partial<IEditorProxy> = {
return false
}
const key = + '_' + this.baseInfo.id
const key = +'_' + this.baseInfo.id
const label = tag
const type = 'block-context-menu-item'
@@ -192,6 +193,8 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
*/
private _ui = new Map<number, uiState>()
private _fileStorage: LSPluginFileStorage
/**
* handler of before unload plugin
* @private
@@ -226,6 +229,9 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
actor?.reject(e)
}
})
// modules
this._fileStorage = new LSPluginFileStorage(this)
}
async ready (
@@ -400,6 +406,10 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
get DB (): IDBProxy {
return this._makeUserProxy(db)
}
get FileStorage (): LSPluginFileStorage {
return this._fileStorage
}
}
export * from './LSPlugin'

View File

@@ -1,6 +1,7 @@
import { StyleString, UIOptions } from './LSPlugin'
import { PluginLocal } from './LSPlugin.core'
import { snakeCase } from 'snake-case'
import * as path from 'path'
interface IObject {
[key: string]: any;
@@ -13,6 +14,31 @@ declare global {
}
}
export const IS_DEV = process.env.NODE_ENV === 'development'
let _appPathRoot
export async function getAppPathRoot () {
if (_appPathRoot) {
return _appPathRoot
}
return (_appPathRoot =
await invokeHostExportedApi('_callApplication', 'getAppPath')
)
}
export async function getSDKPathRoot () {
if (IS_DEV) {
// TODO: cache in preference file
return localStorage.getItem('LSP_DEV_SDK_ROOT') || 'http://localhost:8080'
}
const appPathRoot = await getAppPathRoot()
return path.join(appPathRoot, 'js')
}
export function isObject (item: any) {
return (item === Object(item) && !Array.isArray(item))
}

View File

@@ -0,0 +1,87 @@
import { LSPluginUser } from '../LSPlugin.user'
export interface IAsyncStorage {
getItem (key: string): Promise<string | undefined>
setItem (key: string, value: string): Promise<void>
removeItem (key: string): Promise<void>
hasItem (key: string): Promise<boolean>
clear (): Promise<void>
}
/**
* A storage based on local files under specific context
*/
class LSPluginFileStorage implements IAsyncStorage {
/**
* @param ctx
*/
constructor (
private ctx: LSPluginUser
) {}
/**
* plugin id
*/
get ctxId () {
return this.ctx.baseInfo.id
}
/**
* @param key A string as file name that support nested directory
* @param value Storage value
*/
setItem (key: string, value: string): Promise<void> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'write-plugin-storage-file',
args: [this.ctxId, key, value]
})
}
/**
* @param key
*/
getItem (key: string): Promise<string | undefined> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'read-plugin-storage-file',
args: [this.ctxId, key]
})
}
/**
* @param key
*/
removeItem (key: string): Promise<void> {
return this.ctx.caller.call(`api:call`, {
method: 'unlink-plugin-storage-file',
args: [this.ctxId, key]
})
}
/**
* Clears the storage
*/
clear (): Promise<void> {
return this.ctx.caller.call(`api:call`, {
method: 'clear-plugin-storage-files',
args: [this.ctxId]
})
}
/**
* @param key
*/
hasItem (key: string): Promise<boolean> {
return this.ctx.caller.callAsync(`api:call`, {
method: 'exist-plugin-storage-file',
args: [this.ctxId, key]
})
}
}
export {
LSPluginFileStorage
}

View File

@@ -1,4 +1,5 @@
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
@@ -16,11 +17,14 @@ module.exports = {
extensions: ['.tsx', '.ts', '.js'],
},
plugins: [
new webpack.ProvidePlugin({
process: 'process/browser',
}),
// new BundleAnalyzerPlugin()
],
output: {
library: "LSPluginEntry",
libraryTarget: "umd",
library: 'LSPluginEntry',
libraryTarget: 'umd',
filename: 'lsplugin.user.js',
path: path.resolve(__dirname, 'dist')
},

View File

@@ -18,6 +18,12 @@
(defmethod handle :mkdir [_window [_ dir]]
(fs/mkdirSync dir))
(defmethod handle :mkdir-recur [_window [_ dir]]
(fs/mkdirSync dir #js {:recursive true}))
(defmethod handle :rmdir-recur [_window [_ dir]]
(fs/rmdirSync dir #js {:recursive true}))
;; {encoding: 'utf8', withFileTypes: true}
(defn- readdir
[dir]

View File

@@ -43,6 +43,10 @@
[dir]
(protocol/mkdir! (get-fs dir) dir))
(defn mkdir-recur!
[dir]
(protocol/mkdir-recur! (get-fs dir) dir))
(defn readdir
[dir]
(protocol/readdir (get-fs dir) dir))

View File

@@ -65,12 +65,14 @@
protocol/Fs
(mkdir! [this dir]
(ipc/ipc "mkdir" dir))
(mkdir-recur! [this dir]
(ipc/ipc "mkdir-recur" dir))
(readdir [this dir] ; recursive
(ipc/ipc "readdir" dir))
(unlink! [this path _opts]
(ipc/ipc "unlink" path))
(rmdir! [this dir]
nil)
(ipc/ipc "rmdir-recur" dir))
(read-file [this dir path _options]
(let [path (concat-path dir path)]
(ipc/ipc "readFile" path)))

View File

@@ -2,6 +2,7 @@
(defprotocol Fs
(mkdir! [this dir])
(mkdir-recur! [this dir])
(readdir [this dir])
(unlink! [this path opts])
(rmdir! [this dir])

View File

@@ -93,16 +93,84 @@
path (util/node-path.join path "package.json")]
(fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true}))))
(defn ^:private write_dotdir_file!
[file content sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
exist? (fs/file-exists? path "")
_ (when-not exist? (fs/mkdir-recur! path))
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "write file denied")))
user-path-root (util/node-path.dirname user-path)
exist? (fs/file-exists? user-path-root "")
_ (when-not exist? (fs/mkdir-recur! user-path-root))
_ (fs/write-file! repo "" user-path content {:skip-mtime? true})]
user-path))
(defn ^:private read_dotdir_file
[file sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "read file denied")))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
content (fs/read-file "" user-path)]
content))
(defn ^:private unlink_dotdir_file!
[file sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "access file denied")))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
_ (fs/unlink! user-path {})]))
(def ^:export write_user_tmp_file
(fn [file content]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path "tmp")
exist? (fs/file-exists? path "")
_ (when-not exist? (fs/mkdir! path))
path (util/node-path.join path file)
_ (fs/write-file! repo "" path content {:skip-mtime? true})]
path)))
(write_dotdir_file! file content "tmp")))
(def ^:export write_plugin_storage_file
(fn [plugin-id file content]
(write_dotdir_file!
file content
(let [plugin-id (util/node-path.basename plugin-id)]
(util/node-path.join "storages" plugin-id)))))
(def ^:export read_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(read_dotdir_file
file (util/node-path.join "storages" plugin-id)))))
(def ^:export unlink_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(unlink_dotdir_file!
file (util/node-path.join "storages" plugin-id)))))
(def ^:export exist_plugin_storage_file
(fn [plugin-id file]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)
exist? (fs/file-exists?
(util/node-path.join root "storages" plugin-id)
file)]
exist?)))
(def ^:export clear_plugin_storage_files
(fn [plugin-id]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)]
(fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
(def ^:export load_user_preferences
(fn []