refactor(ui): plugins connector is now multi-project

This commit is contained in:
Guillaume Chau
2018-07-05 13:11:51 +02:00
parent 090c52d81b
commit abd7306fef
10 changed files with 173 additions and 120 deletions

View File

@@ -5,7 +5,6 @@ const sharedData = require('../connectors/shared-data')
const views = require('../connectors/views')
const suggestions = require('../connectors/suggestions')
const folders = require('../connectors/folders')
const cwd = require('../connectors/cwd')
const progress = require('../connectors/progress')
// Utils
const ipc = require('../util/ipc')
@@ -22,12 +21,13 @@ const { validateSuggestion } = require('./suggestion')
const { validateProgress } = require('./progress')
class PluginApi {
constructor ({ plugins }, context) {
constructor ({ plugins, file }, context) {
// Context
this.context = context
this.pluginId = null
this.project = null
this.plugins = plugins
this.cwd = file
// Hooks
this.hooks = {
projectOpen: [],
@@ -362,8 +362,7 @@ class PluginApi {
*/
hasPlugin (id) {
if (['vue-router', 'vuex'].includes(id)) {
const folder = cwd.get()
const pkg = folders.readPackage(folder, this.context, true)
const pkg = folders.readPackage(this.cwd, this.context, true)
return ((pkg.dependencies && pkg.dependencies[id]) || (pkg.devDependencies && pkg.devDependencies[id]))
}
return this.plugins.some(p => matchesPluginId(id, p.id))
@@ -402,7 +401,7 @@ class PluginApi {
* Get current working directory.
*/
getCwd () {
return cwd.get()
return this.cwd()
}
/**
@@ -410,7 +409,7 @@ class PluginApi {
* @param {string} file Path to file relative to project
*/
resolve (file) {
return path.resolve(cwd.get(), file)
return path.resolve(this.cwd, file)
}
/**

View File

@@ -18,7 +18,7 @@ const fileTypes = ['js', 'json', 'yaml']
let current = {}
function list (context) {
return plugins.getApi().configurations
return plugins.getApi(cwd.get()).configurations
}
function findOne (id, context) {
@@ -183,13 +183,17 @@ async function getPromptTabs (id, context) {
}
await prompts.start()
plugins.callHook('configRead', [{
config,
data,
onReadData,
tabs,
cwd: cwd.get()
}], context)
plugins.callHook({
id: 'configRead',
args: [{
config,
data,
onReadData,
tabs,
cwd: cwd.get()
}],
file: cwd.get()
}, context)
return tabs
}
@@ -252,12 +256,16 @@ async function save (id, context) {
writeData({ config, data, changedFields }, context)
plugins.callHook('configWrite', [{
config,
data,
changedFields,
cwd: cwd.get()
}], context)
plugins.callHook({
id: 'configWrite',
args: [{
config,
data,
changedFields,
cwd: cwd.get()
}],
file: cwd.get()
}, context)
current = {}
}

View File

@@ -1,6 +1,7 @@
const path = require('path')
const fs = require('fs')
const LRU = require('lru-cache')
const chalk = require('chalk')
// Subs
const channels = require('../channels')
// Connectors
@@ -47,14 +48,13 @@ const logoCache = new LRU({
// Local
let currentPluginId
let eventsInstalled = false
let plugins = []
let pluginApi
let installationStep
let projectId
let pluginsStore = new Map()
let pluginApiInstances = new Map()
function list (file, context, resetApi = true) {
async function list (file, context, resetApi = true) {
const pkg = folders.readPackage(file, context)
plugins = []
let plugins = []
plugins = plugins.concat(findPlugins(pkg.devDependencies || {}))
plugins = plugins.concat(findPlugins(pkg.dependencies || {}))
@@ -66,11 +66,14 @@ function list (file, context, resetApi = true) {
plugins.unshift(service)
}
if (resetApi) resetPluginApi(context)
pluginsStore.set(file, plugins)
if (resetApi || !pluginApiInstances.get(file)) await resetPluginApi({ file }, context)
return plugins
}
function findOne (id, context) {
function findOne ({ id, file }, context) {
const plugins = getPlugins(file)
return plugins.find(
p => p.id === id
)
@@ -90,54 +93,77 @@ function findPlugins (deps) {
)
}
function resetPluginApi (context) {
// Clean up
if (pluginApi) {
pluginApi.views.forEach(r => views.remove(r.id, context))
pluginApi.ipcHandlers.forEach(fn => ipc.off(fn))
}
sharedData.unWatchAll()
clientAddons.clear(context)
suggestions.clear(context)
setTimeout(() => {
const projects = require('./projects')
const project = projects.getCurrent(context)
pluginApi = new PluginApi({
plugins
}, context)
if (projects.getType(project) !== 'vue') return
// Run Plugin API
runPluginApi(path.resolve(__dirname, '../../'), context, 'ui-defaults')
plugins.forEach(plugin => runPluginApi(plugin.id, context))
runPluginApi(cwd.get(), context, 'vue-cli-ui')
// Add client addons
pluginApi.clientAddons.forEach(options => clientAddons.add(options, context))
// Add views
pluginApi.views.forEach(view => views.add(view, context))
if (!project) return
projectId = project.id
pluginApi.project = project
if (projectId !== project.id) {
callHook('projectOpen', [project, projects.getLast(context)], context)
} else {
callHook('pluginReload', [project], context)
// View open hook
const currentView = views.getCurrent()
if (currentView) views.open(currentView.id)
}
})
return true
function getPlugins (file) {
const plugins = pluginsStore.get(file)
if (!plugins) return []
return plugins
}
function runPluginApi (id, context, fileName = 'ui') {
function resetPluginApi ({ file }, context) {
return new Promise((resolve, reject) => {
let pluginApi = pluginApiInstances.get(file)
let projectId
// Clean up
if (pluginApi) {
projectId = pluginApi.project && pluginApi.project.id
pluginApi.views.forEach(r => views.remove(r.id, context))
pluginApi.ipcHandlers.forEach(fn => ipc.off(fn))
}
sharedData.unWatchAll()
clientAddons.clear(context)
suggestions.clear(context)
// Cyclic dependency with projects connector
setTimeout(() => {
const projects = require('./projects')
const project = projects.getCurrent(context)
const plugins = getPlugins(file)
pluginApi = new PluginApi({
plugins,
file
}, context)
pluginApiInstances.set(file, pluginApi)
if (projects.getType(project) !== 'vue') return
// Run Plugin API
runPluginApi(path.resolve(__dirname, '../../'), pluginApi, context, 'ui-defaults')
plugins.forEach(plugin => runPluginApi(plugin.id, pluginApi, context))
runPluginApi(cwd.get(), pluginApi, context, 'vue-cli-ui')
// Add client addons
pluginApi.clientAddons.forEach(options => clientAddons.add(options, context))
// Add views
pluginApi.views.forEach(view => views.add(view, context))
if (!project) return
pluginApi.project = project
if (projectId !== project.id) {
callHook({
id: 'projectOpen',
args: [project, projects.getLast(context)],
file
}, context)
} else {
callHook({
id: 'pluginReload',
args: [project],
file
}, context)
// View open hook
const currentView = views.getCurrent()
if (currentView) views.open(currentView.id)
}
resolve(true)
})
})
}
function runPluginApi (id, pluginApi, context, fileName = 'ui') {
let module
try {
module = loadModule(`${id}/${fileName}`, cwd.get(), true)
@@ -149,7 +175,7 @@ function runPluginApi (id, context, fileName = 'ui') {
if (module) {
pluginApi.pluginId = id
module(pluginApi)
log('Plugin API loaded for', id)
log('Plugin API loaded for', id, chalk.grey(pluginApi.cwd))
pluginApi.pluginId = null
}
@@ -160,7 +186,14 @@ function runPluginApi (id, context, fileName = 'ui') {
} catch (e) {}
}
function callHook (id, args, context) {
function getApi (folder) {
const pluginApi = pluginApiInstances.get(folder)
if (!pluginApi) throw new Error(`No plugin API available for ${folder}`)
return pluginApi
}
function callHook ({ id, args, file }, context) {
const pluginApi = getApi(file)
const fns = pluginApi.hooks[id]
log(`Hook ${id}`, fns.length, 'handlers')
fns.forEach(fn => fn(...args))
@@ -287,7 +320,7 @@ function runInvoke (id, context) {
await invoke(id, prompts.getAnswers(), cwd.get())
}
// Run plugin api
runPluginApi(id, context)
runPluginApi(id, getApi(cwd.get()), context)
installationStep = 'diff'
notify({
@@ -327,7 +360,7 @@ function update (id, context) {
args: [id]
})
currentPluginId = id
const plugin = findOne(id, context)
const plugin = findOne({ id, file: cwd.get() }, context)
const { current, wanted } = await dependencies.getVersion(plugin, context)
await updatePackage(cwd.get(), getCommand(cwd.get()), null, id)
@@ -343,11 +376,11 @@ function update (id, context) {
icon: 'done'
})
resetPluginApi(context)
await resetPluginApi({ file: cwd.get() }, context)
dependencies.invalidatePackage(id, context)
currentPluginId = null
return findOne(id)
return findOne({ id, file: cwd.get() }, context)
})
}
@@ -387,17 +420,15 @@ async function updateAll (context) {
icon: 'done'
})
resetPluginApi(context)
await resetPluginApi({ file: cwd.get() }, context)
return updatedPlugins
})
}
function getApi () {
return pluginApi
}
async function callAction ({ id, params, file = cwd.get() }, context) {
const pluginApi = getApi(file)
async function callAction ({ id, params }, context) {
context.pubsub.publish(channels.PLUGIN_ACTION_CALLED, {
pluginActionCalled: { id, params }
})

View File

@@ -29,20 +29,22 @@ function getTasks (file = null) {
return list
}
function list ({ file = null, api = true } = {}, context) {
async function list ({ file = null, api = true } = {}, context) {
if (!file) file = cwd.get()
let list = getTasks(file)
const pkg = folders.readPackage(file, context)
if (pkg.scripts) {
const existing = new Map()
await plugins.list(file, context, false)
// Get current valid tasks in project `package.json`
let currentTasks = Object.keys(pkg.scripts).map(
name => {
const id = `${file}:${name}`
existing.set(id, true)
const command = pkg.scripts[name]
const moreData = api ? plugins.getApi().getDescribedTask(command) : null
const moreData = api ? plugins.getApi(file).getDescribedTask(command) : null
return {
id,
name,
@@ -57,7 +59,7 @@ function list ({ file = null, api = true } = {}, context) {
)
if (api) {
currentTasks = currentTasks.concat(plugins.getApi().addedTasks.map(
currentTasks = currentTasks.concat(plugins.getApi(file).addedTasks.map(
task => {
const id = `${file}:${task.name}`
existing.set(id, true)
@@ -399,14 +401,18 @@ async function run (id, context) {
})
}
plugins.callHook('taskExit', [{
task,
args,
child,
cwd: cwd.get(),
signal,
code
}], context)
plugins.callHook({
id: 'taskExit',
args: [{
task,
args,
child,
cwd: cwd.get(),
signal,
code
}],
file: cwd.get()
}, context)
}
child.on('exit', onExit)
@@ -420,12 +426,16 @@ async function run (id, context) {
})
}
plugins.callHook('taskRun', [{
task,
args,
child,
cwd: cwd.get()
}], context)
plugins.callHook({
id: 'taskRun',
args: [{
task,
args,
child,
cwd: cwd.get()
}],
file: cwd.get()
}, context)
}
return task
}
@@ -462,10 +472,14 @@ function clearLogs (id, context) {
function open (id, context) {
const task = findOne(id, context)
plugins.callHook('taskOpen', [{
task,
cwd: cwd.get()
}], context)
plugins.callHook({
id: 'taskOpen',
args: [{
task,
cwd: cwd.get()
}],
file: cwd.get()
}, context)
return true
}

View File

@@ -130,10 +130,14 @@ function open (id, context) {
const view = findOne(id)
currentView = view
const plugins = require('./plugins')
plugins.callHook('viewOpen', [{
view,
cwd: cwd.get()
}], context)
plugins.callHook({
id: 'viewOpen',
args: [{
view,
cwd: cwd.get()
}],
file: cwd.get()
}, context)
return true
}

View File

@@ -1,5 +1,6 @@
const gql = require('graphql-tag')
// Connectors
const cwd = require('../connectors/cwd')
const configurations = require('../connectors/configurations')
const plugins = require('../connectors/plugins')
@@ -35,7 +36,7 @@ type ConfigurationTab {
exports.resolvers = {
Configuration: {
tabs: (configuration, args, context) => configurations.getPromptTabs(configuration.id, context),
plugin: (configuration, args, context) => plugins.findOne(configuration.pluginId, context)
plugin: (configuration, args, context) => plugins.findOne({ id: configuration.pluginId, file: cwd.get() }, context)
},
Query: {

View File

@@ -77,7 +77,7 @@ exports.resolvers = {
Query: {
pluginInstallation: (root, args, context) => plugins.getInstallation(context),
plugins: (root, args, context) => plugins.list(cwd.get(), context),
plugin: (root, { id }, context) => plugins.findOne(id, context)
plugin: (root, { id }, context) => plugins.findOne({ id, file: cwd.get() }, context)
},
Mutation: {
@@ -88,7 +88,7 @@ exports.resolvers = {
pluginUpdate: (root, { id }, context) => plugins.update(id, context),
pluginActionCall: (root, args, context) => plugins.callAction(args, context),
pluginsUpdate: (root, args, context) => plugins.updateAll(context),
pluginResetApi: (root, args, context) => plugins.resetPluginApi(context)
pluginResetApi: (root, args, context) => plugins.resetPluginApi({ file: cwd.get() }, context)
},
Subscription: {

View File

@@ -84,7 +84,7 @@ exports.resolvers = {
Project: {
type: (project, args, context) => projects.getType(project),
plugins: (project, args, context) => plugins.list(project.path, context),
tasks: (project, args, context) => tasks.list({ file: project.path, api: false }, context),
tasks: (project, args, context) => tasks.list({ file: project.path }, context),
homepage: (project, args, context) => projects.getHomepage(project, context)
},

View File

@@ -71,7 +71,7 @@ type TaskView {
exports.resolvers = {
Task: {
prompts: (task, args, context) => tasks.getPrompts(task.id, context),
plugin: (task, args, context) => plugins.findOne(task.pluginId, context),
plugin: (task, args, context) => plugins.findOne({ id: task.pluginId, file: task.path }, context),
project: (task, args, context) => projects.findByPath(task.path, context)
},

View File

@@ -1,15 +1,11 @@
#import "./projectFragment.gql"
#import "./taskFragment.gql"
query projects {
projects {
...project
tasks {
id
name
status
project {
id
}
...task
}
}
}