feat(ui): plugins update

This commit is contained in:
Guillaume Chau
2018-03-16 20:01:58 +01:00
parent 1441c02524
commit 7571e80739
15 changed files with 266 additions and 84 deletions
+1
View File
@@ -25,6 +25,7 @@
"lru-cache": "^4.1.2",
"mkdirp": "^0.5.1",
"rimraf": "^2.6.2",
"semver": "^5.5.0",
"shortid": "^2.2.8",
"subscriptions-transport-ws": "^0.9.5",
"vue": "^2.5.13",
@@ -1,12 +1,16 @@
<template>
<div class="content-view">
<div class="header">
<h2 v-if="title" class="title">{{ title }}</h2>
<slot name="header"/>
<div class="wrapper">
<h2 v-if="title" class="title">{{ title }}</h2>
<slot name="header"/>
</div>
</div>
<div class="content">
<slot/>
<div class="wrapper">
<slot/>
</div>
</div>
</div>
</template>
@@ -32,12 +36,19 @@ export default {
grid-template-rows auto 1fr
grid-template-areas "header" "content"
.wrapper
width 100%
height 100%
box-sizing border-box
.header
grid-area header
h-box()
box-center()
background $vue-ui-color-light-neutral
padding $padding-item
background darken($vue-ui-color-light-neutral, 3%)
.wrapper
background $vue-ui-color-light-neutral
h-box()
box-center()
padding $padding-item
.title
flex 100% 1 1
@@ -49,7 +60,14 @@ export default {
.content
grid-area content
position relative
overflow-x hidden
overflow-y auto
background darken($color-light-background, 3%)
.wrapper
background $color-light-background
position relative
overflow-x hidden
overflow-y auto
&.limit-width
.wrapper
max-width 1200px
</style>
@@ -1,59 +1,122 @@
<template>
<div class="project-plugin-item list-item">
<ItemLogo :image="plugin.logo"/>
<div class="content">
<ItemLogo :image="pluginLogo && pluginLogo.logo"/>
<ListItemInfo
:name="plugin.id"
:link="plugin.website"
show-description
>
<span slot="description" class="plugin-description">
<span class="info version">
<span class="label">{{ $t('components.project-plugin-item.version') }}</span>
<span class="value">{{ plugin.version.current }}</span>
</span>
<ListItemInfo
:name="plugin.id"
:link="plugin.website"
show-description
>
<span slot="description" class="plugin-description">
<template v-if="pluginDetails">
<span class="info version">
<span class="label">{{ $t('components.project-plugin-item.version') }}</span>
<span class="value">{{ pluginDetails.version.current }}</span>
</span>
<span class="info latest">
<span class="label">{{ $t('components.project-plugin-item.latest') }}</span>
<VueIcon
v-if="plugin.version.current !== plugin.version.latest"
icon="warning"
class="top medium"
/>
<span class="value">{{ plugin.version.latest }}</span>
</span>
<span class="info latest">
<span class="label">{{ $t('components.project-plugin-item.latest') }}</span>
<VueIcon
v-if="pluginDetails.version.current !== pluginDetails.version.latest"
icon="warning"
class="top medium"
/>
<span class="value">{{ pluginDetails.version.latest }}</span>
</span>
</template>
<span v-if="plugin.official" class="info">
<VueIcon
icon="star"
class="top medium"
/>
{{ $t('components.project-plugin-item.official') }}
</span>
<span v-if="plugin.official" class="info">
<VueIcon
icon="star"
class="top medium"
/>
{{ $t('components.project-plugin-item.official') }}
</span>
<span v-if="plugin.installed" class="info">
<VueIcon
icon="check_circle"
class="top medium"
/>
{{ $t('components.project-plugin-item.installed') }}
</span>
<span v-if="plugin.installed" class="info">
<VueIcon
icon="check_circle"
class="top medium"
/>
{{ $t('components.project-plugin-item.installed') }}
</span>
<span v-if="plugin.description" class="package-description">
{{ plugin.description }}
<span v-if="pluginDetails && pluginDetails.description" class="package-description">
{{ pluginDetails.description }}
</span>
</span>
</span>
</ListItemInfo>
</ListItemInfo>
<VueButton
v-if="pluginDetails && pluginDetails.version.current !== pluginDetails.version.wanted"
icon-left="file_download"
class="icon-button"
v-tooltip="$t('components.project-plugin-item.actions.update', { target: plugin.id })"
:loading-left="updating"
@click="updatePlugin()"
/>
</div>
</div>
</template>
<script>
import PLUGIN_DETAILS from '../graphql/pluginDetails.gql'
import PLUGIN_LOGO from '../graphql/pluginLogo.gql'
import PLUGIN_UPDATE from '../graphql/pluginUpdate.gql'
export default {
props: {
plugin: {
type: Object,
required: true
}
},
data () {
return {
pluginDetails: null,
pluginLogo:! null,
updating: false
}
},
apollo: {
pluginDetails: {
query: PLUGIN_DETAILS,
variables () {
return {
id: this.plugin.id
}
},
fetchPolicy: 'cache-and-network'
},
pluginLogo: {
query: PLUGIN_LOGO,
variables () {
return {
id: this.plugin.id
}
}
}
},
methods: {
async updatePlugin () {
this.updating = true
try {
this.$apollo.mutate({
mutation: PLUGIN_UPDATE,
variables: {
id: this.plugin.id
}
})
} catch (e) {
console.error(e)
}
this.updating = false
}
}
}
</script>
@@ -63,8 +126,10 @@ export default {
.project-plugin-item
padding $padding-item
h-box()
box-center()
.content
h-box()
box-center()
.list-item-info
flex 100% 1 1
@@ -1,6 +1,7 @@
const path = require('path')
const fs = require('fs')
const LRU = require('lru-cache')
const semver = require('semver')
const {
isPlugin,
isOfficialPlugin,
@@ -11,7 +12,8 @@ const getPackageVersion = require('@vue/cli/lib/util/getPackageVersion')
const {
progress: installProgress,
installPackage,
uninstallPackage
uninstallPackage,
updatePackage
} = require('@vue/cli/lib/util/installDeps')
const { loadOptions } = require('@vue/cli/lib/options')
const invoke = require('@vue/cli/lib/invoke')
@@ -20,6 +22,7 @@ const cwd = require('./cwd')
const folders = require('./folders')
const prompts = require('./prompts')
const progress = require('./progress')
const logs = require('./logs')
const metadataCache = new LRU({
max: 200,
@@ -34,9 +37,12 @@ const PROGRESS_ID = 'plugin-installation'
let currentPluginId
let eventsInstalled = false
let plugins = []
function getPath (id) {
return path.join(cwd.get(), 'node_modules', id)
return path.dirname(require.resolve(id, {
paths: [cwd.get()]
}))
}
function findPlugins (deps) {
@@ -55,12 +61,18 @@ function findPlugins (deps) {
function list (file, context) {
const pkg = folders.readPackage(file, context)
let plugins = []
plugins = []
plugins = plugins.concat(findPlugins(pkg.dependencies || {}))
plugins = plugins.concat(findPlugins(pkg.devDependencies || {}))
return plugins
}
function findOne (id, context) {
return plugins.find(
p => p.id === id
)
}
function readPackage (id, context) {
return folders.readPackage(getPath(id), context)
}
@@ -70,18 +82,10 @@ async function getMetadata (id, context) {
if (metadata) {
return metadata
}
if (isOfficialPlugin(id)) {
const res = await getPackageVersion('vue-cli-version-marker', 'latest')
if (res.statusCode === 200) {
metadata = res.body
}
const pkg = folders.readPackage(path.dirname(require.resolve(id)), context)
metadata.description = pkg.description
} else {
const res = await getPackageVersion(id, id.indexOf('@') === -1 ? 'latest' : '')
if (res.statusCode === 200) {
metadata = res.body
}
const res = await getPackageVersion(id)
if (res.statusCode === 200) {
metadata = res.body
}
if (metadata) {
@@ -98,20 +102,22 @@ async function getVersion ({ id, installed, versionRange }, context) {
} else {
current = null
}
let latest
let latest, wanted
const metadata = await getMetadata(id, context)
if (metadata) {
latest = (metadata['dist-tags'] && metadata['dist-tags'].latest) || metadata.version
latest = metadata['dist-tags'].latest
const versions = Object.keys(metadata.versions)
wanted = semver.maxSatisfying(versions, versionRange)
}
if (!latest) {
// fallback to local version
latest = current
}
if (!latest) latest = current
if (!wanted) wanted = current
return {
current,
latest,
wanted,
range: versionRange
}
}
@@ -230,13 +236,41 @@ async function initPrompts (id, context) {
prompts.start()
}
function update (id, context) {
return progress.wrap('plugin-update', context, async setProgress => {
setProgress({
status: 'plugin-update',
args: [id]
})
currentPluginId = id
const plugin = findOne(id, context)
const { current, wanted } = await getVersion(plugin, context)
const packageManager = loadOptions().packageManager || (hasYarn() ? 'yarn' : 'npm')
await updatePackage(cwd.get(), packageManager, null, id)
logs.add({
message: `Plugin ${id} updated from ${current} to ${wanted}`,
type: 'info'
}, context)
currentPluginId = null
return findOne(id)
})
}
module.exports = {
list,
findOne,
getVersion,
getDescription,
getLogo,
getInstallation,
install,
uninstall,
update,
runInvoke
}
@@ -43,7 +43,8 @@ module.exports = {
projects: (root, args, context) => projects.list(context),
projectCurrent: (root, args, context) => projects.getCurrent(context),
projectCreation: (root, args, context) => projects.getCreation(context),
pluginInstallation: (root, args, context) => plugins.getInstallation(context)
pluginInstallation: (root, args, context) => plugins.getInstallation(context),
plugin: (root, { id }, context) => plugins.findOne(id, context)
},
Mutation: {
@@ -64,7 +65,8 @@ module.exports = {
projectCwdReset: (root, args, context) => projects.resetCwd(context),
pluginInstall: (root, { id }, context) => plugins.install(id, context),
pluginUninstall: (root, { id }, context) => plugins.uninstall(id, context),
pluginInvoke: (root, { id }, context) => plugins.runInvoke(id, context)
pluginInvoke: (root, { id }, context) => plugins.runInvoke(id, context),
pluginUpdate: (root, { id }, context) => plugins.update(id, context)
},
Subscription: {
@@ -69,6 +69,7 @@ type ProjectCreation {
type Version {
current: String
latest: String
wanted: String
range: String
}
@@ -164,6 +165,7 @@ type Query {
projectCurrent: Project
projectCreation: ProjectCreation
pluginInstallation: PluginInstallation
plugin (id: ID!): Plugin
}
type Mutation {
@@ -183,6 +185,7 @@ type Mutation {
pluginInstall (id: ID!): PluginInstallation
pluginUninstall (id: ID!): PluginInstallation
pluginInvoke (id: ID!): PluginInstallation
pluginUpdate (id: ID!): Plugin
}
type Subscription {
@@ -0,0 +1,11 @@
#import "./versionFragment.gql"
query pluginDetails ($id: ID!) {
pluginDetails: plugin (id: $id) {
id
version {
...version
}
description
}
}
@@ -1,13 +1,6 @@
fragment plugin on Plugin {
id
version {
current
latest
range
}
official
installed
website
description
logo
}
@@ -0,0 +1,6 @@
query pluginLogo ($id: ID!) {
pluginLogo: plugin (id: $id) {
id
logo
}
}
@@ -0,0 +1,10 @@
#import "./versionFragment.gql"
mutation pluginUpdate ($id: ID!) {
pluginUpdate (id: $id) {
id
version {
...version
}
}
}
@@ -0,0 +1,6 @@
fragment version on Version {
current
latest
wanted
range
}
+6 -2
View File
@@ -48,7 +48,10 @@
"version": "version",
"latest": "latest",
"official": "Official",
"installed": "Installed"
"installed": "Installed",
"actions": {
"update": "Update {target}"
}
},
"prompts-list": {
"empty": "No configuration"
@@ -79,7 +82,8 @@
"done": "Successfully created project",
"plugin-install": "Installing {arg0}...",
"plugin-uninstall": "Uninstalling {arg0}...",
"plugin-invoke": "Invoking {arg0}..."
"plugin-invoke": "Invoking {arg0}...",
"plugin-update": "Updating {arg0}..."
}
},
"views": {
+6 -2
View File
@@ -48,7 +48,10 @@
"version": "version",
"latest": "dernière version",
"official": "Officiel",
"installed": "Installé"
"installed": "Installé",
"actions": {
"update": "Mettre à jour {target}"
}
},
"prompts-list": {
"empty": "Pas de configuration"
@@ -79,7 +82,8 @@
"done": "Projet créé avec succès",
"plugin-install": "Installation de {arg0}...",
"plugin-uninstall": "Désinstallation de {arg0}...",
"plugin-invoke": "Invocation de {arg0}..."
"plugin-invoke": "Invocation de {arg0}...",
"plugin-update": "Mise à jour de {arg0}..."
}
},
"views": {
@@ -2,6 +2,7 @@
<div class="project-plugins page">
<ContentView
:title="$t('views.project-plugins.title')"
class="limit-width"
>
<template slot="header">
<VueButton
@@ -34,6 +35,10 @@
</template>
</ApolloQuery>
</ContentView>
<ProgressScreen
progress-id="plugin-update"
/>
</div>
</template>
+20
View File
@@ -288,3 +288,23 @@ exports.uninstallPackage = async function (targetDir, command, cliRegistry, pack
await executeCommand(command, args, targetDir)
}
exports.updatePackage = async function (targetDir, command, cliRegistry, packageName) {
const args = []
if (command === 'npm') {
args.push('update', '--loglevel', 'error')
} else if (command === 'yarn') {
args.push('upgrade')
} else {
throw new Error(`Unknown package manager: ${command}`)
}
await addRegistryToArgs(command, args, cliRegistry)
args.push(packageName)
debug(`command: `, command)
debug(`args: `, args)
await executeCommand(command, args, targetDir)
}