chore: merge dev

This commit is contained in:
Guillaume Chau
2018-03-05 20:33:35 +01:00
18 changed files with 242 additions and 132 deletions
+55 -2
View File
@@ -3,6 +3,7 @@
- [Installation](#installation)
- [Usage](#usage)
- [Creating a New Project](#creating-a-new-project)
- [Presets](#presets)
- [Zero-config Prototyping](#zero-config-prototyping)
- [Installing Plugins in an Existing Project](#installing-plugins-in-an-existing-project)
- [Inspecting the webpack Config](#inspecting-the-projects-webpack-config)
@@ -59,9 +60,61 @@ vue create my-project
<img width="682px" src="https://raw.githubusercontent.com/vuejs/vue-cli/dev/docs/screenshot.png">
</p>
#### Presets
### Presets
After you've selected features, you can optionally save it as a preset so that you can reuse it for future projects. If you want to delete a saved preset, you can do that by editing `~/.vuerc`.
After you've selected features, you can optionally save it as a preset so that you can reuse it for future projects. If you want to delete or tweak a saved preset, you can do that by editing `~/.vuerc`.
A preset is defined in JSON. If you have saved a preset via the command line and then open `~/.vuerc`, you will find something like the following:
``` json
{
"useConfigFiles": true,
"router": true,
"vuex": true,
"cssPreprocessor": "sass",
"plugins": {
"@vue/cli-plugin-babel": {},
"@vue/cli-plugin-eslint": {
"config": "airbnb",
"lintOn": ["save", "commit"]
}
}
}
```
The preset data is used by plugin generators to generate corresponding project files. In addition to the above fields, you can also add additional configuration for integrated tools:
``` js
{
"useConfigFiles": true,
"plugins": {...},
"configs": {
"vue": {...},
"postcss": {...},
"eslintConfig": {...},
"jest": {...}
}
}
```
These additional configurations will be merged into `package.json` or corresponding config files, depending on the value of `useConfigFiles`. For example, with `"useConfigFiles": true`, the value of `configs.vue` will be merged into `vue.config.js`.
#### Remote Presets
You can share a preset with other developers by publishing it in a git repo. The repo should contain a `preset.json` file containing the preset data. You can then use the `--preset` option to use the remote preset when creating a project:
``` sh
# use preset from GitHub repo
vue create --preset username/repo my-project
```
GitLab and BitBucket are also supported. Make sure to use the `--clone` option if fetching from private repos:
``` sh
vue create --preset gitlab:username/repo --clone my-project
vue create --preset bitbucket:username/repo --clone my-project
```
### Zero-config Prototyping
+1 -1
View File
@@ -46,7 +46,7 @@
"debug": "^3.1.0",
"eslint": "^4.16.0",
"eslint-plugin-vue-libs": "^2.1.0",
"globby": "^7.1.1",
"globby": "^8.0.1",
"http-server": "^0.11.1",
"jest": "^22.1.4",
"lerna": "^2.8.0",
@@ -66,4 +66,9 @@ module.exports = (api, options) => {
devDependencies: deps[options.cssPreprocessor]
})
}
// additional tooling configurations
if (options.configs) {
api.extendPackage(options.configs)
}
}
@@ -40,3 +40,20 @@ exports.matchesPluginId = (input, full) => {
short === input.replace(scopeRE, '')
)
}
exports.getPluginLink = id => {
if (officialRE.test(id)) {
return `https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-${
exports.toShortPluginId(id)
}`
}
let pkg = {}
try {
pkg = require(`${id}/package.json`)
} catch (e) {}
return (
pkg.homepage ||
(pkg.repository && pkg.repository.url) ||
`https://www.npmjs.com/package/${id.replace(`/`, `%2F`)}`
)
}
@@ -2,7 +2,9 @@ const Generator = require('@vue/cli/lib/Generator')
module.exports = async function generateWithPlugin (plugin, pkg) {
process.env.VUE_CLI_SKIP_WRITE = true
const generator = new Generator('/', pkg || {}, [].concat(plugin))
const generator = new Generator('/', {
plugins: [].concat(plugin)
})
await generator.generate()
return {
pkg: generator.pkg,
@@ -9,7 +9,7 @@ module.exports = async function launchPuppeteer (url) {
const page = await browser.newPage()
const logs = []
page.on('console', msg => logs.push(msg.text))
page.on('console', msg => logs.push(msg.text()))
await page.goto(url)
+1 -1
View File
@@ -22,6 +22,6 @@
},
"dependencies": {
"execa": "^0.8.0",
"puppeteer": "^0.13.0"
"puppeteer": "^1.0.0"
}
}
+61 -52
View File
@@ -44,25 +44,28 @@ qux($1)
test('api: extendPackage', async () => {
const generator = new Generator('/', {
name: 'hello',
list: [1],
vue: {
foo: 1,
bar: 2
}
}, [{
id: 'test',
apply: api => {
api.extendPackage({
name: 'hello2',
list: [2],
vue: {
foo: 2,
baz: 3
}
})
}
}])
pkg: {
name: 'hello',
list: [1],
vue: {
foo: 1,
bar: 2
}
},
plugins: [{
id: 'test',
apply: api => {
api.extendPackage({
name: 'hello2',
list: [2],
vue: {
foo: 2,
baz: 3
}
})
}
}]
})
await generator.generate()
@@ -79,14 +82,17 @@ test('api: extendPackage', async () => {
})
test('api: extendPackage function', async () => {
const generator = new Generator('/', { foo: 1 }, [{
id: 'test',
apply: api => {
api.extendPackage(pkg => ({
foo: pkg.foo + 1
}))
}
}])
const generator = new Generator('/', {
pkg: { foo: 1 },
plugins: [{
id: 'test',
apply: api => {
api.extendPackage(pkg => ({
foo: pkg.foo + 1
}))
}
}]
})
await generator.generate()
@@ -97,7 +103,7 @@ test('api: extendPackage function', async () => {
})
test('api: extendPackage merge dependencies', async () => {
const generator = new Generator('/', {}, [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
@@ -120,7 +126,7 @@ test('api: extendPackage merge dependencies', async () => {
})
}
}
])
] })
await generator.generate()
@@ -135,7 +141,7 @@ test('api: extendPackage merge dependencies', async () => {
})
test('api: warn invalid dep range', async () => {
new Generator('/', {}, [
new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
@@ -146,7 +152,7 @@ test('api: warn invalid dep range', async () => {
})
}
}
])
] })
expect(logs.warn.some(([msg]) => {
return (
@@ -157,7 +163,7 @@ test('api: warn invalid dep range', async () => {
})
test('api: extendPackage dependencies conflict', async () => {
new Generator('/', {}, [
new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
@@ -178,7 +184,7 @@ test('api: extendPackage dependencies conflict', async () => {
})
}
}
])
] })
expect(logs.warn.some(([msg]) => {
return (
@@ -191,7 +197,7 @@ test('api: extendPackage dependencies conflict', async () => {
})
test('api: render fs directory', async () => {
const generator = new Generator('/', {}, [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
@@ -201,7 +207,7 @@ test('api: render fs directory', async () => {
n: 1
}
}
])
] })
await generator.generate()
@@ -212,7 +218,7 @@ test('api: render fs directory', async () => {
})
test('api: render object', async () => {
const generator = new Generator('/', {}, [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
@@ -225,7 +231,7 @@ test('api: render object', async () => {
n: 2
}
}
])
] })
await generator.generate()
@@ -234,7 +240,7 @@ test('api: render object', async () => {
})
test('api: render middleware', async () => {
const generator = new Generator('/', {}, [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: (api, options) => {
@@ -247,7 +253,7 @@ test('api: render middleware', async () => {
n: 3
}
}
])
] })
await generator.generate()
@@ -256,7 +262,7 @@ test('api: render middleware', async () => {
})
test('api: hasPlugin', () => {
new Generator('/', {}, [
new Generator('/', { plugins: [
{
id: 'foo',
apply: api => {
@@ -275,32 +281,35 @@ test('api: hasPlugin', () => {
id: '@vue/cli-plugin-baz',
apply: () => {}
}
])
] })
})
test('api: onCreateComplete', () => {
const fn = () => {}
const cbs = []
new Generator('/', {}, [
{
id: 'test',
apply: api => {
api.onCreateComplete(fn)
new Generator('/', {
plugins: [
{
id: 'test',
apply: api => {
api.onCreateComplete(fn)
}
}
}
], cbs)
],
completeCbs: cbs
})
expect(cbs).toContain(fn)
})
test('api: resolve', () => {
new Generator('/foo/bar', {}, [
new Generator('/foo/bar', { plugins: [
{
id: 'test',
apply: api => {
expect(api.resolve('baz')).toBe(path.resolve('/foo/bar', 'baz'))
}
}
])
] })
})
test('extract config files', async () => {
@@ -322,14 +331,14 @@ test('extract config files', async () => {
}
}
const generator = new Generator('/', {}, [
const generator = new Generator('/', { plugins: [
{
id: 'test',
apply: api => {
api.extendPackage(configs)
}
}
])
] })
await generator.generate({
extractConfigFiles: true
+1 -1
View File
@@ -1,4 +1,4 @@
jest.setTimeout(12000)
jest.setTimeout(20000)
jest.mock('inquirer')
const invoke = require('../lib/invoke')
+10 -9
View File
@@ -36,24 +36,17 @@ program
program
.command('create <app-name>')
.description('create a new project powered by vue-cli-service')
.option('-p, --preset <presetName>', 'Skip prompts and use saved preset')
.option('-p, --preset <presetName>', 'Skip prompts and use saved or remote preset')
.option('-d, --default', 'Skip prompts and use default preset')
.option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
.option('-m, --packageManager <command>', 'Use specified npm client when installing dependencies')
.option('-r, --registry <url>', 'Use specified npm registry when installing dependencies (only for npm)')
.option('-f, --force', 'Overwrite target directory if it exists')
.option('-c, --clone', 'Use git clone when fetching remote preset')
.action((name, cmd) => {
require('../lib/create')(name, cleanArgs(cmd))
})
program
.command('invoke <plugin> [pluginOptions]')
.allowUnknownOption()
.description('invoke the generator of a plugin in an already created project')
.action((plugin) => {
require('../lib/invoke')(plugin, minimist(process.argv.slice(3)))
})
program
.command('add <plugin> [pluginOptions]')
.allowUnknownOption()
@@ -62,6 +55,14 @@ program
require('../lib/add')(plugin, minimist(process.argv.slice(3)))
})
program
.command('invoke <plugin> [pluginOptions]')
.allowUnknownOption()
.description('invoke the generator of a plugin in an already created project')
.action((plugin) => {
require('../lib/invoke')(plugin, minimist(process.argv.slice(3)))
})
program
.command('inspect [paths...]')
.option('--mode <mode>')
+23 -8
View File
@@ -13,6 +13,7 @@ const PromptModuleAPI = require('./PromptModuleAPI')
const writeFileTree = require('./util/writeFileTree')
const formatFeatures = require('./util/formatFeatures')
const setupDevProject = require('./util/setupDevProject')
const fetchRemotePreset = require('./util/fetchRemotePreset')
const {
defaults,
@@ -60,7 +61,7 @@ module.exports = class Creator {
let preset
if (cliOptions.preset) {
// vue create foo --preset bar
preset = this.resolvePreset(cliOptions.preset)
preset = await this.resolvePreset(cliOptions.preset, cliOptions.clone)
} else if (cliOptions.default) {
// vue create foo --default
preset = defaults.presets.default
@@ -132,12 +133,11 @@ module.exports = class Creator {
log()
log(`🚀 Invoking generators...`)
const plugins = this.resolvePlugins(preset.plugins)
const generator = new Generator(
context,
const generator = new Generator(context, {
pkg,
plugins,
createCompleteCbs
)
completeCbs: createCompleteCbs
})
await generator.generate({
extractConfigFiles: preset.useConfigFiles
})
@@ -194,7 +194,7 @@ module.exports = class Creator {
let preset
if (answers.preset && answers.preset !== '__manual__') {
preset = this.resolvePreset(answers.preset)
preset = await this.resolvePreset(answers.preset)
} else {
// manual
preset = {
@@ -218,9 +218,24 @@ module.exports = class Creator {
return preset
}
resolvePreset (name) {
async resolvePreset (name, clone) {
let preset
const savedPresets = loadOptions().presets || {}
let preset = savedPresets[name]
if (name.includes('/')) {
logWithSpinner(`Fetching remote preset ${chalk.cyan(name)}...`)
try {
preset = await fetchRemotePreset(name, clone)
stopSpinner()
} catch (e) {
stopSpinner()
error(`Failed fetching remote preset ${chalk.cyan(name)}:`)
throw e
}
} else {
preset = savedPresets[name]
}
// use default preset if user has not overwritten it
if (name === 'default' && !preset) {
preset = defaults.presets.default
+7 -2
View File
@@ -17,7 +17,12 @@ const logTypes = {
}
module.exports = class Generator {
constructor (context, pkg, plugins, completeCbs = []) {
constructor (context, {
pkg = {},
plugins = [],
completeCbs = [],
files = {}
} = {}) {
this.context = context
this.plugins = plugins
this.originalPkg = pkg
@@ -27,7 +32,7 @@ module.exports = class Generator {
// for conflict resolution
this.depSources = {}
// virtual file tree
this.files = {}
this.files = files
this.fileMiddlewares = []
this.postProcessFilesCbs = []
// exit messages
+5 -23
View File
@@ -5,25 +5,12 @@ const globby = require('globby')
const isBinary = require('isbinaryfile')
const yaml = require('yaml-front-matter')
const mergeDeps = require('./util/mergeDeps')
const { isOfficialPlugin, toShortPluginId } = require('@vue/cli-shared-utils')
const { getPluginLink, toShortPluginId } = require('@vue/cli-shared-utils')
const isString = val => typeof val === 'string'
const isFunction = val => typeof val === 'function'
const isObject = val => val && typeof val === 'object'
// get link for a 3rd party plugin.
function getLink (id) {
let pkg = {}
try {
pkg = require(`${id}/package.json`)
} catch (e) {}
return (
pkg.homepage ||
(pkg.repository && pkg.repository.url) ||
`https://www.npmjs.com/package/${id.replace(`/`, `%2F`)}`
)
}
class GeneratorAPI {
/**
* @param {string} id - Id of the owner plugin
@@ -39,15 +26,10 @@ class GeneratorAPI {
this.pluginsData = generator.plugins
.filter(({ id }) => id !== `@vue/cli-service`)
.map(({ id }) => {
const name = toShortPluginId(id)
return {
name: name,
link: isOfficialPlugin(id)
? `https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-${name}`
: getLink(id)
}
})
.map(({ id }) => ({
name: toShortPluginId(id),
link: getPluginLink(id)
}))
}
/**
+20 -5
View File
@@ -2,6 +2,7 @@ const fs = require('fs')
const path = require('path')
const execa = require('execa')
const chalk = require('chalk')
const globby = require('globby')
const resolve = require('resolve')
const inquirer = require('inquirer')
const Generator = require('./Generator')
@@ -27,6 +28,20 @@ function load (request, context) {
}
}
async function readFiles (context) {
const files = await globby(['**'], {
cwd: context,
onlyFiles: true,
gitignore: true,
ignore: ['**node_modules**']
})
const res = {}
for (const file of files) {
res[file] = fs.readFileSync(path.resolve(context, file), 'utf-8')
}
return res
}
async function invoke (pluginName, options = {}, context = process.cwd()) {
delete options._
const pkgPath = path.resolve(context, 'package.json')
@@ -81,12 +96,12 @@ async function invoke (pluginName, options = {}, context = process.cwd()) {
}
const createCompleteCbs = []
const generator = new Generator(
context,
const generator = new Generator(context, {
pkg,
[plugin],
createCompleteCbs
)
plugins: [plugin],
files: await readFiles(context),
completeCbs: createCompleteCbs
})
log()
logWithSpinner('🚀', `Invoking generator for ${id}...`)
+2 -1
View File
@@ -15,7 +15,8 @@ const presetSchema = createSchema(joi => joi.object().keys({
router: joi.boolean(),
vuex: joi.boolean(),
cssPreprocessor: joi.string().only(['sass', 'less', 'stylus']),
plugins: joi.object().required()
plugins: joi.object().required(),
configs: joi.object()
}))
const schema = createSchema(joi => joi.object().keys({
@@ -0,0 +1,27 @@
module.exports = function fetchRemotePreset (name, clone) {
// github shorthand fastpath
if (!clone && /^[\w_-]+\/[\w_-]+$/.test(name)) {
const { get } = require('./request')
return get(`https://raw.githubusercontent.com/${name}/master/preset.json`)
.then(res => res.body)
}
// fallback to full download
const os = require('os')
const path = require('path')
const download = require('download-git-repo')
const tmpdir = path.join(os.tmpdir(), 'vue-cli')
return new Promise((resolve, reject) => {
download(name, tmpdir, { clone }, err => {
if (err) return reject(err)
let preset
try {
preset = require(path.join(tmpdir, 'preset.json'))
} catch (e) {
return reject(e)
}
resolve(preset)
})
})
}
+2 -1
View File
@@ -31,9 +31,10 @@
"chalk": "^2.3.0",
"commander": "^2.12.2",
"debug": "^3.1.0",
"download-git-repo": "^1.0.2",
"ejs": "^2.5.7",
"execa": "^0.8.0",
"globby": "^7.1.1",
"globby": "^8.0.1",
"import-global": "^0.1.0",
"inquirer": "^4.0.1",
"isbinaryfile": "^3.0.2",
+1 -24
View File
@@ -3422,17 +3422,7 @@ dot-prop@^3.0.0:
dependencies:
is-obj "^1.0.0"
dot-prop@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
dependencies:
is-obj "^1.0.0"
dotenv@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-0.4.0.tgz#f6fb351363c2d92207245c737802c9ab5ae1495a"
download-git-repo@^1.0.1:
download-git-repo@^1.0.1, download-git-repo@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/download-git-repo/-/download-git-repo-1.0.2.tgz#0b93a62057e41e2f21b1a06c95e7b26362b108ff"
dependencies:
@@ -8537,19 +8527,6 @@ punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
puppeteer@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-0.13.0.tgz#2e6956205f2c640964c2107f620ae1eef8bde8fd"
dependencies:
debug "^2.6.8"
extract-zip "^1.6.5"
https-proxy-agent "^2.1.0"
mime "^1.3.4"
progress "^2.0.0"
proxy-from-env "^1.0.0"
rimraf "^2.6.1"
ws "^3.0.0"
puppeteer@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.1.1.tgz#adbf25e49f5ef03443c10ab8e09a954ca0c7bfee"