mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-04-20 03:00:57 -05:00
refactor(ui): plugins connector is now multi-project
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 = {}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
#import "./projectFragment.gql"
|
||||
#import "./taskFragment.gql"
|
||||
|
||||
query projects {
|
||||
projects {
|
||||
...project
|
||||
tasks {
|
||||
id
|
||||
name
|
||||
status
|
||||
project {
|
||||
id
|
||||
}
|
||||
...task
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user