feat(ui): new hooks for plugins

This commit is contained in:
Guillaume Chau
2018-06-11 01:53:01 +02:00
parent 8fae98e7ce
commit 8ba6bcfe7b
16 changed files with 297 additions and 25 deletions

View File

@@ -1075,6 +1075,66 @@ api.onPluginReload((project) => {
})
```
### onConfigRead
Called when a configuration screen is open or refreshed.
```js
api.onConfigRead(({ config, data, onReadData, tabs, cwd }) => {
console.log(config.id)
})
```
### onConfigWrite
Called when the user saves in a configuration screen.
```js
api.onConfigWrite(({ config, data, changedFields, cwd }) => {
// ...
})
```
### onTaskOpen
Called when the user open a task details pane.
```js
api.onTaskOpen(({ task, cwd }) => {
console.log(task.id)
})
```
### onTaskRun
Called when the user run a task.
```js
api.onTaskRun(({ task, args, child, cwd }) => {
// ...
})
```
### onTaskExit
Called when a task exists. It can be called both called on success or failure.
```js
api.onTaskExit(({ task, args, child, signal, code, cwd }) => {
// ...
})
```
### onViewOpen
Called when the users open a view (like 'Plugins', 'Configurations' or 'Tasks').
```js
api.onViewOpen(({ view, cwd }) => {
console.log(view.id)
})
```
## Public static files
You may need to expose some static files over the cli-ui builtin HTTP server (typically if you want to specify an icon to a custom view).

View File

@@ -1,13 +1,20 @@
module.exports = {
root: true,
extends: [
'plugin:vue/essential',
'@vue/standard'
],
globals: {
ClientAddonApi: false
},
plugins: [
'graphql'
]
],
rules: {
'vue/html-self-closing': 'error'
}
}

View File

@@ -7,7 +7,7 @@
>
<div class="content">
<VueGroup
v-model="currentView"
v-model="currentViewName"
class="vertical small-indicator left-indicator primary"
indicator
>
@@ -30,6 +30,7 @@ import VIEWS from '../graphql/views.gql'
import VIEW_ADDED from '../graphql/viewAdded.gql'
import VIEW_REMOVED from '../graphql/viewRemoved.gql'
import VIEW_CHANGED from '../graphql/viewChanged.gql'
import VIEW_OPEN from '../graphql/viewOpen.gql'
export default {
data () {
@@ -90,12 +91,16 @@ export default {
},
computed: {
currentView: {
currentView () {
const currentRoute = this.$route
return this.views.find(
item => isIncludedRoute(currentRoute, this.$router.resolve({ name: item.name }).route)
)
},
currentViewName: {
get () {
const currentRoute = this.$route
const view = this.views.find(
item => isIncludedRoute(currentRoute, this.$router.resolve({ name: item.name }).route)
)
const view = this.currentView
return view && view.name
},
set (name) {
@@ -104,6 +109,22 @@ export default {
}
}
}
},
watch: {
currentView: {
handler (value, oldValue) {
if (!value) return
if (oldValue && value.id === oldValue.id) return
this.$apollo.mutate({
mutation: VIEW_OPEN,
variables: {
id: value.id
}
})
},
immediate: true
}
}
}
</script>

View File

@@ -24,7 +24,7 @@
</div>
<div class="view">
<div ref="render" class="xterm-render"></div>
<div ref="render" class="xterm-render"/>
</div>
<resize-observer v-if="autoSize" @notify="fit"/>

View File

@@ -21,8 +21,16 @@ class PluginApi {
this.project = null
this.plugins = plugins
// Hooks
this.projectOpenHooks = []
this.pluginReloadHooks = []
this.hooks = {
projectOpen: [],
pluginReload: [],
configRead: [],
configWrite: [],
taskRun: [],
taskExit: [],
taskOpen: [],
viewOpen: []
}
// Data
this.configurations = []
this.describedTasks = []
@@ -43,7 +51,7 @@ class PluginApi {
cb(this.project)
return
}
this.projectOpenHooks.push(cb)
this.hooks.projectOpen.push(cb)
}
/**
@@ -52,7 +60,61 @@ class PluginApi {
* @param {function} cb Handler
*/
onPluginReload (cb) {
this.pluginReloadHooks.push(cb)
this.hooks.pluginReload.push(cb)
}
/**
* Register an handler called when a config is red.
*
* @param {function} cb Handler
*/
onConfigRead (cb) {
this.hooks.configRead.push(cb)
}
/**
* Register an handler called when a config is written.
*
* @param {function} cb Handler
*/
onConfigWrite (cb) {
this.hooks.configWrite.push(cb)
}
/**
* Register an handler called when a task is run.
*
* @param {function} cb Handler
*/
onTaskRun (cb) {
this.hooks.taskRun.push(cb)
}
/**
* Register an handler called when a task has exited.
*
* @param {function} cb Handler
*/
onTaskExit (cb) {
this.hooks.taskExit.push(cb)
}
/**
* Register an handler called when the user opens one task details.
*
* @param {function} cb Handler
*/
onTaskOpen (cb) {
this.hooks.taskOpen.push(cb)
}
/**
* Register an handler called when a view is open.
*
* @param {function} cb Handler
*/
onViewOpen (cb) {
this.hooks.viewOpen.push(cb)
}
/**

View File

@@ -156,18 +156,18 @@ async function getPromptTabs (id, context) {
}
// API
const configData = await config.onRead({
const onReadData = await config.onRead({
cwd: cwd.get(),
data
})
let tabs = configData.tabs
let tabs = onReadData.tabs
if (!tabs) {
tabs = [
{
id: '__default',
label: 'Default',
prompts: configData.prompts
prompts: onReadData.prompts
}
]
}
@@ -178,10 +178,19 @@ async function getPromptTabs (id, context) {
tabId: tab.id
}))
}
if (configData.answers) {
await prompts.setAnswers(configData.answers)
if (onReadData.answers) {
await prompts.setAnswers(onReadData.answers)
}
await prompts.start()
plugins.callHook('configRead', [{
config,
data,
onReadData,
tabs,
cwd: cwd.get()
}], context)
return tabs
}
return []
@@ -242,6 +251,14 @@ async function save (id, context) {
})
writeData({ config, data, changedFields }, context)
plugins.callHook('configWrite', [{
config,
data,
changedFields,
cwd: cwd.get()
}], context)
current = {}
}
}

View File

@@ -118,12 +118,14 @@ function resetPluginApi (context) {
if (!project) return
if (projectId !== project.id) {
projectId = project.id
log('Hook onProjectOpen', pluginApi.projectOpenHooks.length, 'handlers')
pluginApi.projectOpenHooks.forEach(fn => fn(project, projects.getLast(context)))
callHook('projectOpen', [project, projects.getLast(context)], context)
pluginApi.project = project
} else {
log('Hook onPluginReload', pluginApi.pluginReloadHooks.length, 'handlers')
pluginApi.pluginReloadHooks.forEach(fn => fn(project))
callHook('pluginReload', [project], context)
// View open hook
const currentView = views.getCurrent()
if (currentView) views.open(currentView.id)
}
})
}
@@ -147,6 +149,12 @@ function runPluginApi (id, context, fileName = 'ui') {
} catch (e) {}
}
function callHook (id, args, context) {
const fns = pluginApi.hooks[id]
log(`Hook ${id}`, fns.length, 'handlers')
fns.forEach(fn => fn(...args))
}
function findOne (id, context) {
return plugins.find(
p => p.id === id
@@ -472,5 +480,6 @@ module.exports = {
getApi,
finishInstall,
callAction,
callHook,
serve
}

View File

@@ -348,6 +348,15 @@ async function run (id, context) {
icon: 'done'
})
}
plugins.callHook('taskExit', [{
task,
args,
child,
cwd: cwd.get(),
signal,
code
}], context)
}
child.on('exit', onExit)
@@ -360,6 +369,13 @@ async function run (id, context) {
cwd: cwd.get()
})
}
plugins.callHook('taskRun', [{
task,
args,
child,
cwd: cwd.get()
}], context)
}
return task
}
@@ -394,6 +410,15 @@ function clearLogs (id, context) {
return task
}
function open (id, context) {
const task = findOne(id, context)
plugins.callHook('taskOpen', [{
task,
cwd: cwd.get()
}], context)
return true
}
module.exports = {
list,
findOne,
@@ -401,5 +426,6 @@ module.exports = {
run,
stop,
updateOne,
clearLogs
clearLogs,
open
}

View File

@@ -3,6 +3,8 @@ const cwd = require('./cwd')
// Subs
const channels = require('../channels')
let currentView
function createViewsSet () {
// Builtin views
return [
@@ -116,6 +118,17 @@ function removeBadge ({ viewId, badgeId }, context) {
}
}
function open (id, context) {
const view = findOne(id)
currentView = view
const plugins = require('./plugins')
plugins.callHook('viewOpen', [{
view,
cwd: cwd.get()
}], context)
return true
}
module.exports = {
list,
findOne,
@@ -123,5 +136,7 @@ module.exports = {
remove,
update,
addBadge,
removeBadge
removeBadge,
open,
getCurrent: () => currentView
}

View File

@@ -16,6 +16,7 @@ extend type Mutation {
taskRun (id: ID!): Task
taskStop (id: ID!): Task
taskLogsClear (id: ID!): Task
taskOpen (id: ID!): Boolean
}
extend type Subscription {
@@ -79,7 +80,8 @@ exports.resolvers = {
Mutation: {
taskRun: (root, { id }, context) => tasks.run(id, context),
taskStop: (root, { id }, context) => tasks.stop(id, context),
taskLogsClear: (root, { id }, context) => tasks.clearLogs(id, context)
taskLogsClear: (root, { id }, context) => tasks.clearLogs(id, context),
taskOpen: (root, { id }, context) => tasks.open(id, context)
},
Subscription: {

View File

@@ -9,6 +9,10 @@ extend type Query {
views: [View]
}
extend type Mutation {
viewOpen (id: ID!): Boolean
}
extend type Subscription {
viewAdded: View
viewRemoved: View
@@ -47,6 +51,10 @@ exports.resolvers = {
views: (root, args, context) => views.list(context)
},
Mutation: {
viewOpen: (root, { id }, context) => views.open(id, context)
},
Subscription: {
viewAdded: {
subscribe: (parent, args, { pubsub }) => pubsub.asyncIterator(channels.VIEW_ADDED)

View File

@@ -0,0 +1,3 @@
mutation taskOpen ($id: ID!) {
taskOpen (id: $id)
}

View File

@@ -0,0 +1,3 @@
mutation viewOpen ($id: ID!) {
viewOpen (id: $id)
}

View File

@@ -141,6 +141,7 @@ import TASK_RUN from '../graphql/taskRun.gql'
import TASK_STOP from '../graphql/taskStop.gql'
import TASK_LOGS_CLEAR from '../graphql/taskLogsClear.gql'
import TASK_LOG_ADDED from '../graphql/taskLogAdded.gql'
import TASK_OPEN from '../graphql/taskOpen.gql'
export default {
name: 'ProjectTaskDetails',
@@ -251,10 +252,24 @@ export default {
this.showParameters = false
this.currentView = '_output'
this.$_init = false
this.open()
}
},
mounted () {
this.open()
},
methods: {
open () {
this.$apollo.mutate({
mutation: TASK_OPEN,
variables: {
id: this.id
}
})
},
runTask () {
this.$apollo.mutate({
mutation: TASK_RUN,

View File

@@ -39,4 +39,24 @@ module.exports = api => {
return 'meow'
})
// Hooks
api.onViewOpen(({ view }) => {
console.log('onViewOpen', view.id)
})
api.onTaskOpen(({ task }) => {
console.log('onTaskOpen', task.id)
})
api.onTaskRun(({ task }) => {
console.log('onTaskRun', task.id)
})
api.onTaskExit(({ task }) => {
console.log('onTaskExit', task.id)
})
api.onConfigRead(({ config }) => {
console.log('onConfigRead', config.id)
})
api.onConfigWrite(({ config }) => {
console.log('onConfigWrite', config.id)
})
}

View File

@@ -7,11 +7,13 @@ module.exports = {
apolloEngine: false,
graphqlTimeout: 1000000
},
configureWebpack: {
resolve: {
symlinks: false
}
},
chainWebpack: config => {
config.module.rule('stylus').oneOf('vue').use('postcss-loader')
.tap(options =>
@@ -21,5 +23,7 @@ module.exports = {
}
})
)
}
},
lintOnSave: undefined
}