vue-cli源码分析之config命令

724 阅读2分钟

我们先来看下config命令的配置

packages/@vue/cli/bin/vue.js

program
  .command('config [value]')
  .description('inspect and modify the config')
  .option('-g, --get <path>', 'get value from option')
  .option('-s, --set <path> <value>', 'set option value')
  .option('-d, --delete <path>', 'delete option from config')
  .option('-e, --edit', 'open config with default editor')
  .option('--json', 'outputs JSON result only')
  .action((value, options) => {
    require('../lib/config')(value, options)
  })

config命令是用来检查和修改cli的配置的,它有以下选项:

  • '-g, --get ':获取指定路径的配置
  • '-s, --set ':修改指定路径的配置
  • '-d, --delete ':删除指定路径的配置
  • '-e, --edit':使用默认编辑器打开配置文件
  • '--json':将配置以json格式输出

命令处理函数中在../lib/config文件中,我们打开该文件可以看到导出了configure函数。

packages/@vue/cli/lib/config.js

module.exports = (...args) => {
  return configure(...args).catch(err => {
    error(err)
    if (!process.env.VUE_CLI_TEST) {
      process.exit(1)
    }
  })
}

接下来我们来一一分析该函数

const fs = require('fs-extra')
const path = require('path')
const homedir = require('os').homedir()
const { get, set, unset, error, launch } = require('@vue/cli-shared-utils')

const file = path.resolve(homedir, '.vuerc')
const config = await fs.readJson(file)

方法的一开始找到了cli的配置文件.vuerc,然后读取该文件并进行了保存。接下来是一一处理各选项。

--json选项:

if (!options.delete && !options.get && !options.edit && !options.set) {
    if (options.json) {
        console.log(JSON.stringify({
        resolvedPath: file,
        content: config
        }))
    } else {
        console.log('Resolved path: ' + file + '\n', JSON.stringify(config, null, 2))
    }
}

如果命令的选项是json的话,则将配置文件的地址及配置内容放置在一个对象里以JSON字符串的格式输出。如果命令选项是除了deletegeteditset之外的其它选项时,则直接输出配置文件的地址和配置信息。如下:

img3.png

-g, --get <path>选项:

  if (options.get) {
    // eslint-disable-next-line no-shadow
    const value = get(config, options.get)

    if (options.json) {
      console.log(JSON.stringify({
        value
      }))
    } else {
      console.log(value)
    }
  }

get方法

packages/@vue/cli-shared-utils/lib/object.js

exports.get = function (target, path) {
  // 将路径以.进行切割,obj1.obj2 -> [obj1,obj2]
  const fields = path.split('.')
  // 保存传入的全部配置信息
  let obj = target
  const l = fields.length
  // 然后一一递归找到需要查找的配置信息
  for (let i = 0; i < l - 1; i++) {
    const key = fields[i]
    if (!obj[key]) {
      return undefined
    }
    obj = obj[key]
  }
  // 返回找到的配置信息
  return obj[fields[l - 1]]
}

调用get方法,传入配置文件及要查找的配置信息的路径,输出查到到的配置信息,如果命令选项中带有json,则将该配置信息转换为JSON字符串后再输出。如下:

img2.png

-s, --set <path> <value>选项:

if (options.set && !value) {
    throw new Error(`Make sure you define a value for the option ${options.set}`)
}

if (options.set && value) {
    console.log(config, options.set, value)
    set(config, options.set, value)

    if (value.match('[0-9]')) {
        set(config, options.set, parseInt(value))
    }

    if (value === 'true') {
        set(config, options.set, true)
    }

    if (value === 'false') {
        set(config, options.set, false)
    }
    // 重新写入
    await fs.writeFile(file, JSON.stringify(config, null, 2), 'utf-8')
    if (options.json) {
        console.log(JSON.stringify({
        	updated: options.set
        }))
    } else {
        console.log(`You have updated the option: ${options.set} to ${value}`)
    }
}

set方法

packages/@vue/cli-shared-utils/lib/object.js

exports.set = function (target, path, value) {
    // 将路径以.进行切割,obj1.obj2 -> [obj1,obj2]
    const fields = path.split('.')
    // 保存传入的全部配置信息
    let obj = target
    const l = fields.length
    // 然后一一递归找到需要查找的配置信息
    for (let i = 0; i < l - 1; i++) {
        const key = fields[i]
        if (!obj[key]) {
            obj[key] = {}
        }
        obj = obj[key]
    }
    // 修改找到的配置信息
    obj[fields[l - 1]] = value
}

set选项必须要携带配置的新值,不然会报错。由于命令携带的参数均会被转为字符串,所以我们需要单独处理下数字及布尔值。修改完成后,再重新将配置写入.vuerc配置文件,再将更新的信息打印出来。如下:

img4.png

-d, --delete <path>选项:

if (options.delete) {
	unset(config, options.delete)
	await fs.writeFile(file, JSON.stringify(config, null, 2), 'utf-8')
	if (options.json) {
		console.log(JSON.stringify({
			deleted: options.delete
		}))
	} else {
		console.log(`You have removed the option: ${options.delete}`)
	}
}

unset方法

packages/@vue/cli-shared-utils/lib/object.js

exports.unset = function (target, path) {
	// 将路径以.进行切割,obj1.obj2 -> [obj1,obj2]
	const fields = path.split('.')
	// 保存传入的全部配置信息
	let obj = target
	const l = fields.length
	const objs = []
	/*
		将数据以如下格式保存到objs里,删掉obj2之后,obj1则是一个空对象,则把obj1也删掉,
		{
			parent: {
				...,
				obj1: {
					obj2: "value"
				}
			},
			key: 'obj1',
			value: { obj2: "value" }
		}
	*/
	for (let i = 0; i < l - 1; i++) {
		const key = fields[i]
		if (!obj[key]) {
			return
		}
		objs.unshift({ parent: obj, key, value: obj[key] })
		obj = obj[key]
	}
	// 删掉目标对象
	delete obj[fields[l - 1]]
	// 清除空的对象
	for (const { parent, key, value } of objs) {
		if (!Object.keys(value).length) {
			delete parent[key]
		}
	}
}

先将选项中携带的目标对象删掉,修改完成后,再重新将配置写入.vuerc配置文件,再将更新的信息打印出来。如下:

img5.png

-e, --edit选项:

if (options.edit) {
	launch(file)
}

launch方法:

packages/@vue/cli-shared-utils/lib/launch.js

const launch = require('launch-editor')

exports.launch = (...args) => {
  // 要打开的文件路径
  const file = args[0]
  console.log(`Opening ${file}...`)
  // 文件打开之后的回调函数
  let cb = args[args.length - 1]
  if (typeof cb !== 'function') {
    cb = null
  }
  // 用编辑器打开文件
  launch(...args, (fileName, errorMessage) => {
    console.error(`Unable to open '${fileName}'`, errorMessage)
    console.log(`Try setting the EDITOR env variable. More info: https://github.com/yyx990803/launch-editor`)

    if (cb) cb(fileName, errorMessage)
  })
}

以上便是vue config命令的全部解析~