diff --git a/packages/@vue/cli-ui/package.json b/packages/@vue/cli-ui/package.json
index 8c1cbe2a79..039340f98e 100644
--- a/packages/@vue/cli-ui/package.json
+++ b/packages/@vue/cli-ui/package.json
@@ -28,7 +28,6 @@
"lowdb": "^1.0.0",
"lru-cache": "^4.1.2",
"mkdirp": "^0.5.1",
- "parse-diff": "^0.4.2",
"rimraf": "^2.6.2",
"semver": "^5.5.0",
"shortid": "^2.2.8",
@@ -51,7 +50,7 @@
"lint-staged": "^6.0.0",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
- "vue-cli-plugin-apollo": "^0.6.1",
+ "vue-cli-plugin-apollo": "^0.7.1",
"vue-template-compiler": "^2.5.13"
},
"browserslist": [
diff --git a/packages/@vue/cli-ui/src/components/FileDiff.vue b/packages/@vue/cli-ui/src/components/FileDiff.vue
index d9450acca0..7c6a4bc73d 100644
--- a/packages/@vue/cli-ui/src/components/FileDiff.vue
+++ b/packages/@vue/cli-ui/src/components/FileDiff.vue
@@ -27,11 +27,17 @@
-
+
+
+ {{ $t('components.file-diff.binary') }}
+
+
+
+
@@ -102,8 +108,14 @@ status-color($color)
flex 100% 1 1
width 0
- .content
- overflow-x auto
+ .is-binary
+ h-box()
+ box-center()
+ padding $padding-item
+ opacity .5
+
+ .icon
+ margin-right 4px
&.new
status-color($vue-ui-color-success)
diff --git a/packages/@vue/cli-ui/src/components/FileDiffChunk.vue b/packages/@vue/cli-ui/src/components/FileDiffChunk.vue
index 619048be0f..6fb5ca2363 100644
--- a/packages/@vue/cli-ui/src/components/FileDiffChunk.vue
+++ b/packages/@vue/cli-ui/src/components/FileDiffChunk.vue
@@ -25,6 +25,11 @@ export default {
@import "~@/style/imports"
.file-diff-chunk
+ .changes
+ overflow-x auto
+ display grid
+ grid-template-column 1fr
+
&:not(:last-child)
&::after
content '•••'
diff --git a/packages/@vue/cli-ui/src/components/FileDiffView.vue b/packages/@vue/cli-ui/src/components/FileDiffView.vue
index 21cfc1f469..4dfe319184 100644
--- a/packages/@vue/cli-ui/src/components/FileDiffView.vue
+++ b/packages/@vue/cli-ui/src/components/FileDiffView.vue
@@ -115,6 +115,11 @@ import PageVisibility from '../mixins/PageVisibility'
import FILE_DIFFS from '../graphql/fileDiffs.gql'
import GIT_COMMIT from '../graphql/gitCommit.gql'
+const defaultCollapsed = [
+ 'yarn.lock',
+ 'package-lock.json'
+]
+
export default {
mixins: [
PageVisibility
@@ -135,7 +140,18 @@ export default {
fileDiffs: {
query: FILE_DIFFS,
loadingKey: 'loading',
- fetchPolicy: 'cahe-and-network'
+ fetchPolicy: 'cahe-and-network',
+ result () {
+ this.fileDiffs.forEach(fileDiff => {
+ if (typeof this.collapsed[fileDiff.id] === 'undefined' && (
+ fileDiff.binary ||
+ defaultCollapsed.includes(fileDiff.from) ||
+ defaultCollapsed.includes(fileDiff.to)
+ )) {
+ this.$set(this.collapsed, fileDiff.id, true)
+ }
+ })
+ }
}
},
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/git.js b/packages/@vue/cli-ui/src/graphql-api/connectors/git.js
index fca5bd130d..6a707470c3 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/git.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/git.js
@@ -1,18 +1,41 @@
const execa = require('execa')
-const parseDiff = require('parse-diff')
+const parseDiff = require('../utils/parse-diff')
// Connectors
const cwd = require('./cwd')
+async function getNewFiles (context) {
+ const { stdout } = await execa('git', [
+ 'ls-files',
+ '-o',
+ '--exclude-standard',
+ '--full-name'
+ ], {
+ cwd: cwd.get()
+ })
+ if (stdout.trim()) {
+ return stdout.split(/\r?\n/g)
+ }
+ return []
+}
+
async function getDiffs (context) {
- const { stdout } = await execa('git', ['diff', 'HEAD'], {
+ const newFiles = await getNewFiles(context)
+ await execa('git', ['add', '-N', '*'], {
+ cwd: cwd.get()
+ })
+ const { stdout } = await execa('git', ['diff'], {
cwd: cwd.get()
})
- return parseDiff(stdout).map(
+ await reset(context)
+ const list = parseDiff(stdout).map(
fileDiff => ({
id: fileDiff.index.join(' '),
- ...fileDiff
+ ...fileDiff,
+ new: newFiles.includes(fileDiff.to)
})
)
+
+ return list
}
async function commit (message, context) {
@@ -25,7 +48,15 @@ async function commit (message, context) {
return true
}
+async function reset (context) {
+ await execa('git', ['reset'], {
+ cwd: cwd.get()
+ })
+ return true
+}
+
module.exports = {
getDiffs,
- commit
+ commit,
+ reset
}
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js b/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
index bc3644ab0e..2405ced7cd 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
@@ -43,6 +43,7 @@ let currentPluginId
let eventsInstalled = false
let plugins = []
let pluginApi
+let installationStep
function getPath (id) {
return path.dirname(resolveModule(id, cwd.get()))
@@ -181,6 +182,7 @@ function getInstallation (context) {
return {
id: 'plugin-install',
pluginId: currentPluginId,
+ step: installationStep,
prompts: prompts.list()
}
}
@@ -192,8 +194,10 @@ function install (id, context) {
args: [id]
})
currentPluginId = id
+ installationStep = 'install'
await installPackage(cwd.get(), getCommand(), null, id)
await initPrompts(id, context)
+ installationStep = 'config'
return getInstallation(context)
})
}
@@ -204,9 +208,11 @@ function uninstall (id, context) {
status: 'plugin-uninstall',
args: [id]
})
+ installationStep = 'uninstall'
currentPluginId = id
await uninstallPackage(cwd.get(), getCommand(), null, id)
currentPluginId = null
+ installationStep = null
return getInstallation(context)
})
}
@@ -224,11 +230,17 @@ function runInvoke (id, context) {
}
// Run plugin api
runPluginApi(id, context)
- currentPluginId = null
+ installationStep = 'diff'
return getInstallation(context)
})
}
+function finishInstall (context) {
+ installationStep = null
+ currentPluginId = null
+ return getInstallation(context)
+}
+
async function initPrompts (id, context) {
prompts.reset()
try {
@@ -278,5 +290,6 @@ module.exports = {
update,
runInvoke,
resetPluginApi,
- getApi
+ getApi,
+ finishInstall
}
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/prompts.js b/packages/@vue/cli-ui/src/graphql-api/connectors/prompts.js
index 84c05bbd8c..1eacc90723 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/prompts.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/prompts.js
@@ -72,7 +72,9 @@ function getChoices (prompt) {
} else {
result = data
}
- const defaultValue = getDefaultValue(prompt)
+ const defaultValue = prompt.type === 'list' || prompt.type === 'rawlist'
+ ? getDefaultValue(prompt)
+ : undefined
return result.map(
item => generatePromptChoice(prompt, item, defaultValue)
)
diff --git a/packages/@vue/cli-ui/src/graphql-api/resolvers.js b/packages/@vue/cli-ui/src/graphql-api/resolvers.js
index 88033786bd..bb67d43ea8 100644
--- a/packages/@vue/cli-ui/src/graphql-api/resolvers.js
+++ b/packages/@vue/cli-ui/src/graphql-api/resolvers.js
@@ -83,6 +83,7 @@ module.exports = {
pluginInstall: (root, { id }, context) => plugins.install(id, context),
pluginUninstall: (root, { id }, context) => plugins.uninstall(id, context),
pluginInvoke: (root, { id }, context) => plugins.runInvoke(id, context),
+ pluginFinishInstall: (root, args, context) => plugins.finishInstall(context),
pluginUpdate: (root, { id }, context) => plugins.update(id, context),
taskRun: (root, { id }, context) => tasks.run(id, context),
taskStop: (root, { id }, context) => tasks.stop(id, context),
diff --git a/packages/@vue/cli-ui/src/graphql-api/type-defs.js b/packages/@vue/cli-ui/src/graphql-api/type-defs.js
index 9c3a8cffce..abccad3449 100644
--- a/packages/@vue/cli-ui/src/graphql-api/type-defs.js
+++ b/packages/@vue/cli-ui/src/graphql-api/type-defs.js
@@ -97,9 +97,17 @@ type Plugin {
type PluginInstallation {
id: ID!
pluginId: ID
+ step: PluginInstallationStep
prompts: [Prompt]
}
+enum PluginInstallationStep {
+ install
+ uninstall
+ config
+ diff
+}
+
type Feature implements DescribedEntity {
id: ID!
name: String
@@ -207,6 +215,7 @@ type FileDiff {
to: String
new: Boolean
deleted: Boolean
+ binary: Boolean
chunks: [FileDiffChunk]
}
@@ -269,6 +278,7 @@ type Mutation {
pluginInstall (id: ID!): PluginInstallation
pluginUninstall (id: ID!): PluginInstallation
pluginInvoke (id: ID!): PluginInstallation
+ pluginFinishInstall: PluginInstallation
pluginUpdate (id: ID!): Plugin
taskRun (id: ID!): Task
taskStop (id: ID!): Task
diff --git a/packages/@vue/cli-ui/src/graphql-api/utils/parse-diff.js b/packages/@vue/cli-ui/src/graphql-api/utils/parse-diff.js
new file mode 100644
index 0000000000..49b032928e
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql-api/utils/parse-diff.js
@@ -0,0 +1,192 @@
+// From https://github.com/sergeyt/parse-diff
+module.exports = function (input) {
+ if (!input) { return [] }
+ if (input.match(/^\s+$/)) { return [] }
+
+ const lines = input.split('\n')
+ if (lines.length === 0) { return [] }
+
+ const files = []
+ let file = null
+ let lnDel = 0
+ let lnAdd = 0
+ let current = null
+
+ const start = function (line) {
+ file = {
+ chunks: [],
+ deletions: 0,
+ additions: 0
+ }
+ files.push(file)
+
+ if (!file.to && !file.from) {
+ const fileNames = parseFile(line)
+
+ if (fileNames) {
+ file.from = fileNames[0]
+ file.to = fileNames[1]
+ }
+ }
+ }
+
+ const restart = function () {
+ if (!file || file.chunks.length) { return start() }
+ }
+
+ const newFile = function () {
+ restart()
+ file.new = true
+ file.from = '/dev/null'
+ }
+
+ const deletedFile = function () {
+ restart()
+ file.deleted = true
+ file.to = '/dev/null'
+ }
+
+ const index = function (line) {
+ restart()
+ file.index = line.split(' ').slice(1)
+ }
+
+ const fromFile = function (line) {
+ restart()
+ file.from = parseFileFallback(line)
+ }
+
+ const toFile = function (line) {
+ restart()
+ file.to = parseFileFallback(line)
+ }
+
+ const binary = function (line) {
+ file.binary = true
+ }
+
+ const chunk = function (line, match) {
+ let newStart, oldStart
+ lnDel = (oldStart = +match[1])
+ const oldLines = +(match[2] || 0)
+ lnAdd = (newStart = +match[3])
+ const newLines = +(match[4] || 0)
+ current = {
+ content: line,
+ changes: [],
+ oldStart,
+ oldLines,
+ newStart,
+ newLines
+ }
+ file.chunks.push(current)
+ }
+
+ const del = function (line) {
+ if (!current) return
+ current.changes.push({type: 'del', del: true, ln: lnDel++, content: line})
+ file.deletions++
+ }
+
+ const add = function (line) {
+ if (!current) return
+ current.changes.push({type: 'add', add: true, ln: lnAdd++, content: line})
+ file.additions++
+ }
+
+ const normal = function (line) {
+ if (!current) return
+ current.changes.push({
+ type: 'normal',
+ normal: true,
+ ln1: lnDel++,
+ ln2: lnAdd++,
+ content: line
+ })
+ }
+
+ const eof = function (line) {
+ const recentChange = current.changes[current.changes.length - 1]
+
+ return current.changes.push({
+ type: recentChange.type,
+ [recentChange.type]: true,
+ ln1: recentChange.ln1,
+ ln2: recentChange.ln2,
+ ln: recentChange.ln,
+ content: line
+ })
+ }
+
+ const schema = [
+ // todo beter regexp to avoid detect normal line starting with diff
+ [/^\s+/, normal],
+ [/^diff\s/, start],
+ [/^new file mode \d+$/, newFile],
+ [/^deleted file mode \d+$/, deletedFile],
+ [/^Binary files/, binary],
+ [/^index\s[\da-zA-Z]+\.\.[\da-zA-Z]+(\s(\d+))?$/, index],
+ [/^---\s/, fromFile],
+ [/^\+\+\+\s/, toFile],
+ [/^@@\s+-(\d+),?(\d+)?\s+\+(\d+),?(\d+)?\s@@/, chunk],
+ [/^-/, del],
+ [/^\+/, add],
+ [/^\\ No newline at end of file$/, eof]
+ ]
+
+ const parse = function (line) {
+ for (let p of schema) {
+ const m = line.match(p[0])
+ if (m) {
+ p[1](line, m)
+ return true
+ }
+ }
+ return false
+ }
+
+ for (let line of lines) {
+ parse(line)
+ }
+
+ return files
+}
+
+function parseFile (s) {
+ if (!s) return
+
+ const result = /\sa\/(.*)\sb\/(.*)/.exec(s)
+
+ return [result[1], result[2]]
+}
+
+// fallback function to overwrite file.from and file.to if executed
+function parseFileFallback (s) {
+ s = ltrim(s, '-')
+ s = ltrim(s, '+')
+ s = s.trim()
+ // ignore possible time stamp
+ const t = (/\t.*|\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d(.\d+)?\s(\+|-)\d\d\d\d/).exec(s)
+ if (t) { s = s.substring(0, t.index).trim() }
+ // ignore git prefixes a/ or b/
+ if (s.match((/^(a|b)\//))) { return s.substr(2) } else { return s }
+}
+
+function ltrim (s, chars) {
+ s = makeString(s)
+ if (!chars && trimLeft) { return trimLeft.call(s) }
+ chars = defaultToWhiteSpace(chars)
+ return s.replace(new RegExp(`^${chars}+`), '')
+}
+
+const makeString = s => s === null ? '' : s + ''
+
+const { trimLeft } = String.prototype
+
+function defaultToWhiteSpace (chars) {
+ if (chars === null) { return '\\s' }
+ if (chars.source) { return chars.source }
+ return `[${escapeRegExp(chars)}]`
+}
+
+const escapeRegExp = s => makeString(s).replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1')
diff --git a/packages/@vue/cli-ui/src/graphql/fileDiffs.gql b/packages/@vue/cli-ui/src/graphql/fileDiffs.gql
index 08517a056f..544925b859 100644
--- a/packages/@vue/cli-ui/src/graphql/fileDiffs.gql
+++ b/packages/@vue/cli-ui/src/graphql/fileDiffs.gql
@@ -5,6 +5,7 @@ query fileDiffs {
to
new
deleted
+ binary
chunks {
oldStart
oldLines
diff --git a/packages/@vue/cli-ui/src/graphql/pluginFinishInstall.gql b/packages/@vue/cli-ui/src/graphql/pluginFinishInstall.gql
new file mode 100644
index 0000000000..eb067ff9c6
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql/pluginFinishInstall.gql
@@ -0,0 +1,7 @@
+#import "./pluginInstallationFragment.gql"
+
+mutation pluginFinishInstall {
+ pluginFinishInstall {
+ ...pluginInstallation
+ }
+}
diff --git a/packages/@vue/cli-ui/src/graphql/pluginInstallationFragment.gql b/packages/@vue/cli-ui/src/graphql/pluginInstallationFragment.gql
index cd867ee574..a757832c5d 100644
--- a/packages/@vue/cli-ui/src/graphql/pluginInstallationFragment.gql
+++ b/packages/@vue/cli-ui/src/graphql/pluginInstallationFragment.gql
@@ -3,6 +3,7 @@
fragment pluginInstallation on PluginInstallation {
id
pluginId
+ step
prompts {
...prompt
}
diff --git a/packages/@vue/cli-ui/src/locales/en.json b/packages/@vue/cli-ui/src/locales/en.json
index 2f4e7fc98d..30ff8af188 100644
--- a/packages/@vue/cli-ui/src/locales/en.json
+++ b/packages/@vue/cli-ui/src/locales/en.json
@@ -1,5 +1,8 @@
{
"components": {
+ "file-diff": {
+ "binary": "Binary file not shown"
+ },
"file-diff-view": {
"files-changed": "Files changed",
"search-file": "Search file",
@@ -251,6 +254,9 @@
"cancel": "Cancel",
"finish": "Finish installation"
}
+ },
+ "diff": {
+ "label": "Files changed"
}
},
"modal": {
diff --git a/packages/@vue/cli-ui/src/locales/fr.json b/packages/@vue/cli-ui/src/locales/fr.json
index 38b0ff2ca8..77c1cc6efa 100644
--- a/packages/@vue/cli-ui/src/locales/fr.json
+++ b/packages/@vue/cli-ui/src/locales/fr.json
@@ -1,5 +1,8 @@
{
"components": {
+ "file-diff": {
+ "binary": "Fichier binaire non affiché"
+ },
"file-diff-view": {
"files-changed": "Fichiers modifiés",
"search-file": "Rechercher un fichier",
@@ -251,6 +254,9 @@
"cancel": "Annuler",
"finish": "Terminer l'installation"
}
+ },
+ "diff": {
+ "label": "Fichers modifiés"
}
},
"modal": {
diff --git a/packages/@vue/cli-ui/src/views/ProjectPluginsAdd.vue b/packages/@vue/cli-ui/src/views/ProjectPluginsAdd.vue
index 681049dd49..ccbe1ea004 100644
--- a/packages/@vue/cli-ui/src/views/ProjectPluginsAdd.vue
+++ b/packages/@vue/cli-ui/src/views/ProjectPluginsAdd.vue
@@ -112,6 +112,18 @@
/>
+
+