mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-04-20 03:00:57 -05:00
feat(ui): ProjectNav plugin support
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div class="test-view">
|
||||
<h1>I'm a custom view</h1>
|
||||
<p>A vue-cli plugin created me! I am a dynamically loaded component paired with a custom route.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.test-view
|
||||
padding 42px
|
||||
</style>
|
||||
@@ -1,8 +1,13 @@
|
||||
import VueProgress from 'vue-progress-path'
|
||||
import WebpackDashboard from './components/WebpackDashboard.vue'
|
||||
import TestView from './components/TestView.vue'
|
||||
|
||||
Vue.use(VueProgress, {
|
||||
defaultShape: 'circle'
|
||||
})
|
||||
|
||||
ClientAddonApi.component('vue-webpack-dashboard', WebpackDashboard)
|
||||
|
||||
ClientAddonApi.addRoutes('vue-webpack', [
|
||||
{ path: '', name: 'test-webpack-route', component: TestView }
|
||||
])
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
// Connectors
|
||||
const logs = require('../connectors/logs')
|
||||
const plugins = require('../connectors/plugins')
|
||||
const sharedData = require('../connectors/shared-data')
|
||||
const routes = require('../connectors/routes')
|
||||
// Utils
|
||||
const ipc = require('../utils/ipc')
|
||||
const { validate: validateConfig } = require('./configuration')
|
||||
const { validate: validateTask } = require('./task')
|
||||
const { validate: validateClientAddon } = require('./client-addon')
|
||||
// Validators
|
||||
const { validateConfiguration } = require('./configuration')
|
||||
const { validateTask } = require('./task')
|
||||
const { validateClientAddon } = require('./client-addon')
|
||||
const { validateRoute, validateBadge } = require('./route')
|
||||
|
||||
class PluginApi {
|
||||
constructor (context) {
|
||||
this.context = context
|
||||
this.pluginId = null
|
||||
// Data
|
||||
this.configurations = []
|
||||
this.tasks = []
|
||||
this.clientAddons = []
|
||||
this.routes = []
|
||||
this.actions = new Map()
|
||||
this.pluginId = null
|
||||
}
|
||||
|
||||
describeConfig (options) {
|
||||
try {
|
||||
validateConfig(options)
|
||||
validateConfiguration(options)
|
||||
this.configurations.push(options)
|
||||
} catch (e) {
|
||||
logs.add({
|
||||
@@ -64,6 +71,42 @@ class PluginApi {
|
||||
}
|
||||
}
|
||||
|
||||
/* Routes */
|
||||
|
||||
addRoute (options) {
|
||||
try {
|
||||
validateRoute(options)
|
||||
this.routes.push(options)
|
||||
} catch (e) {
|
||||
logs.add({
|
||||
type: 'error',
|
||||
tag: 'PluginApi',
|
||||
message: `(${this.pluginId || 'unknown plugin'}) 'addRoute' options are invalid\n${e.message}`
|
||||
}, this.context)
|
||||
console.error(new Error(`Invalid options: ${e.message}`))
|
||||
}
|
||||
}
|
||||
|
||||
addRouteBadge (routeId, options) {
|
||||
try {
|
||||
validateBadge(options)
|
||||
routes.addBadge({ routeId, badge: options }, this.context)
|
||||
} catch (e) {
|
||||
logs.add({
|
||||
type: 'error',
|
||||
tag: 'PluginApi',
|
||||
message: `(${this.pluginId || 'unknown plugin'}) 'addRouteBadge' options are invalid\n${e.message}`
|
||||
}, this.context)
|
||||
console.error(new Error(`Invalid options: ${e.message}`))
|
||||
}
|
||||
}
|
||||
|
||||
removeRouteBadge (routeId, badgeId) {
|
||||
routes.removeBadge({ routeId, badgeId }, this.context)
|
||||
}
|
||||
|
||||
/* IPC */
|
||||
|
||||
ipcOn (cb) {
|
||||
return ipc.on(cb)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ const schema = createSchema(joi => ({
|
||||
url: joi.string()
|
||||
}))
|
||||
|
||||
exports.validate = (options) => {
|
||||
exports.validateClientAddon = (options) => {
|
||||
validateSync(options, schema)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ const schema = createSchema(joi => ({
|
||||
onWrite: joi.func().required()
|
||||
}))
|
||||
|
||||
exports.validate = (options) => {
|
||||
exports.validateConfiguration = (options) => {
|
||||
validateSync(options, schema)
|
||||
}
|
||||
|
||||
25
packages/@vue/cli-ui/src/graphql-api/api/route.js
Normal file
25
packages/@vue/cli-ui/src/graphql-api/api/route.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const { createSchema, validateSync } = require('@vue/cli-shared-utils')
|
||||
|
||||
const routeSchema = createSchema(joi => ({
|
||||
id: joi.string().required(),
|
||||
name: joi.string().required().description('route name (vue-router)'),
|
||||
icon: joi.string().required(),
|
||||
tooltip: joi.string().required()
|
||||
}))
|
||||
|
||||
const badgeSchema = createSchema(joi => ({
|
||||
id: joi.string().required(),
|
||||
type: joi.string(),
|
||||
label: joi.string().required(),
|
||||
count: joi.number().integer(),
|
||||
priority: joi.number().integer(),
|
||||
hidden: joi.boolean()
|
||||
}))
|
||||
|
||||
exports.validateRoute = (options) => {
|
||||
validateSync(options, routeSchema)
|
||||
}
|
||||
|
||||
exports.validateBadge = (options) => {
|
||||
validateSync(options, badgeSchema)
|
||||
}
|
||||
@@ -17,6 +17,6 @@ const schema = createSchema(joi => ({
|
||||
onExit: joi.func()
|
||||
}))
|
||||
|
||||
exports.validate = (options) => {
|
||||
exports.validateTask = (options) => {
|
||||
validateSync(options, schema)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ module.exports = {
|
||||
ROUTE_ADDED: 'route_added',
|
||||
ROUTE_REMOVED: 'route_removed',
|
||||
ROUTE_CHANGED: 'route_changed',
|
||||
VUE_ROUTER_DEFINITION_ADDED: 'vue_router_definition_added',
|
||||
VUE_ROUTER_DEFINITION_REMOVED: 'vue_router_definition_removed',
|
||||
CLIENT_ADDON_ADDED: 'client_addon_added',
|
||||
SHARED_DATA_UPDATED: 'shared_data_updated',
|
||||
PLUGIN_ACTION_CALLED: 'plugin_action_called',
|
||||
|
||||
@@ -26,6 +26,7 @@ const prompts = require('./prompts')
|
||||
const progress = require('./progress')
|
||||
const logs = require('./logs')
|
||||
const clientAddons = require('./client-addons')
|
||||
const routes = require('./routes')
|
||||
// Api
|
||||
const PluginApi = require('../api/PluginApi')
|
||||
// Utils
|
||||
@@ -77,6 +78,11 @@ function list (file, context) {
|
||||
}
|
||||
|
||||
function resetPluginApi (context) {
|
||||
// Clean up
|
||||
if (pluginApi) {
|
||||
pluginApi.routes.forEach(r => routes.remove(r.id, context))
|
||||
}
|
||||
|
||||
pluginApi = new PluginApi(context)
|
||||
// Run Plugin API
|
||||
runPluginApi('@vue/cli-service', context)
|
||||
@@ -84,6 +90,8 @@ function resetPluginApi (context) {
|
||||
runPluginApi('.', context, 'vue-cli-ui')
|
||||
// Add client addons
|
||||
pluginApi.clientAddons.forEach(options => clientAddons.add(options, context))
|
||||
// Add routes
|
||||
pluginApi.routes.forEach(route => routes.add(route, context))
|
||||
}
|
||||
|
||||
function runPluginApi (id, context, fileName = 'ui') {
|
||||
|
||||
@@ -26,8 +26,6 @@ let routes = [
|
||||
...BUILTIN_ROUTES
|
||||
]
|
||||
|
||||
let vueRouterDefinitions = []
|
||||
|
||||
function list (context) {
|
||||
return routes
|
||||
}
|
||||
@@ -64,29 +62,6 @@ function update (route, context) {
|
||||
}
|
||||
}
|
||||
|
||||
function listVueRouterDefinitions (context) {
|
||||
return vueRouterDefinitions
|
||||
}
|
||||
|
||||
function addVueRouterDefinition ({ id, routes }, context) {
|
||||
const def = { id, routes }
|
||||
vueRouterDefinitions.push(def)
|
||||
context.pubsub.publish(channels.VUE_ROUTER_DEFINITION_ADDED, {
|
||||
vueRouterDEfinitionAdded: def
|
||||
})
|
||||
}
|
||||
|
||||
function removeVueRouterDefinition (id, context) {
|
||||
const index = vueRouterDefinitions.findIndex(d => d.id === id)
|
||||
if (index !== -1) {
|
||||
const def = vueRouterDefinitions[index]
|
||||
vueRouterDefinitions.splice(index, 1)
|
||||
context.pubsub.publish(channels.VUE_ROUTER_DEFINITION_REMOVED, {
|
||||
vueRouterDefinitionRemoved: def
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addBadge ({ routeId, badge }, context) {
|
||||
const route = findOne(routeId)
|
||||
if (route) {
|
||||
@@ -94,12 +69,12 @@ function addBadge ({ routeId, badge }, context) {
|
||||
const existingBadge = route.badges.find(b => b.id === badge.id)
|
||||
if (existingBadge) {
|
||||
Object.assign(existingBadge, badge, {
|
||||
count: existingBadge.count + 1
|
||||
count: existingBadge.count + (badge.count || 1)
|
||||
})
|
||||
} else {
|
||||
route.badges.push({
|
||||
type: 'dim',
|
||||
count: 1,
|
||||
count: (badge.count || 1),
|
||||
priority: 0,
|
||||
hidden: false,
|
||||
...badge
|
||||
@@ -130,9 +105,6 @@ module.exports = {
|
||||
add,
|
||||
remove,
|
||||
update,
|
||||
listVueRouterDefinitions,
|
||||
addVueRouterDefinition,
|
||||
removeVueRouterDefinition,
|
||||
addBadge,
|
||||
removeBadge
|
||||
}
|
||||
|
||||
@@ -7,15 +7,12 @@ const routes = require('../connectors/routes')
|
||||
exports.types = gql`
|
||||
extend type Query {
|
||||
routes: [Route]
|
||||
vueRouterDefinitions: [VueRouterDefinition]
|
||||
}
|
||||
|
||||
extend type Subscription {
|
||||
routeAdded: Route
|
||||
routeRemoved: Route
|
||||
routeChanged: Route
|
||||
vueRouterDefinitionAdded: VueRouterDefinition
|
||||
vueRouterDefinitionRemoved: VueRouterDefinition
|
||||
}
|
||||
|
||||
type Route {
|
||||
@@ -43,17 +40,11 @@ enum RouteBadgeType {
|
||||
accent
|
||||
dim
|
||||
}
|
||||
|
||||
type VueRouterDefinition {
|
||||
id: ID!
|
||||
routes: JSON
|
||||
}
|
||||
`
|
||||
|
||||
exports.resolvers = {
|
||||
Query: {
|
||||
routes: (root, args, context) => routes.list(context),
|
||||
vueRouterDefinitions: (root, args, context) => routes.listVueRouterDefinitions(context)
|
||||
routes: (root, args, context) => routes.list(context)
|
||||
},
|
||||
|
||||
Subscription: {
|
||||
@@ -65,12 +56,6 @@ exports.resolvers = {
|
||||
},
|
||||
routeRemoved: {
|
||||
subscribe: (parent, args, { pubsub }) => pubsub.asyncIterator(channels.ROUTE_REMOVED)
|
||||
},
|
||||
vueRouterDefinitionAdded: {
|
||||
subscribe: (parent, args, { pubsub }) => pubsub.asyncIterator(channels.VUE_ROUTER_DEFINITION_ADDED)
|
||||
},
|
||||
vueRouterDefinitionRemoved: {
|
||||
subscribe: (parent, args, { pubsub }) => pubsub.asyncIterator(channels.VUE_ROUTER_DEFINITION_REMOVED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Vue from 'vue'
|
||||
import router from '../router'
|
||||
import ProjectHome from '../views/ProjectHome.vue'
|
||||
|
||||
export default class ClientAddonApi {
|
||||
constructor (addonId) {
|
||||
constructor () {
|
||||
this.components = new Map()
|
||||
this.componentListeners = new Map()
|
||||
}
|
||||
@@ -42,6 +44,20 @@ export default class ClientAddonApi {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
addRoutes (id, routes) {
|
||||
router.addRoutes([
|
||||
{
|
||||
path: `/addon/${id}`,
|
||||
component: ProjectHome,
|
||||
meta: {
|
||||
needProject: true,
|
||||
restore: true
|
||||
},
|
||||
children: routes
|
||||
}
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function toComponentId (id) {
|
||||
|
||||
Reference in New Issue
Block a user