文件门路:VUE 3.0 源码 /script/release.js

该脚本从主函数main()函数的运行开始:

1、应用 prompt CLI提醒插件,疏导用户 Select release type...

      ? Select release type ...      > patch (3.0.0)        minor (3.0.0)        major (3.0.0)        prepatch (3.0.1-rc.0)        preminor (3.1.0-rc.0)        premajor (4.0.0-rc.0)        prerelease (3.0.0-rc.6)        custom

2、疏导用户设置版本号,如果抉择custom自定义版本,会提醒以后的package.json version 为初始值进行编辑。

       √ Select release type · custom       ? Input custom version » 3.0.0-rc.5

3、应用 semver 插件验证版本有效性。semver 全称Semantic Version 版本命名标准,能够比拟两个版本号的大小、验证某个版本号是否非法、提取版本号,例如从“=v1.2.1”体取出"1.2.1"等性能。

4、再次确认版本

? Releasing v3.0.0-rc.6. Confirm? (y/N) » false

5、公布前做单元测试

6、更新 package.json 版本 & 外部依赖模块版本

7、从git元数据生成变更日志,判断有批改信息,进行code提交

8、Pushing to GitHub...

源码正文

/** * minimist 轻量级的命令行参数解析引擎 * process.argv.slice(2) 对应 执行命令参数地位(即第3个起始位,对等下例中"-x 3 -y 4 -n 5 -abc --beep=boop foo bar baz"这部分数据) 实例如下: * node example/parse.js -x 3 -y 4 -n 5 -abc --beep=boop foo bar baz * args 后果为:{ *      _: [ 'foo', 'bar', 'baz' ], *      x: 3, *      y: 4, *      n: 5, *      a: true, *      b: true, *      c: true, *      beep: 'boop' *  } */const args = require('minimist')(process.argv.slice(2))/** nodejs模块——fs模块:fs模块用于对系统文件及目录进行读写操作 */const fs = require('fs')/* nodejs模块:提供文件门路相干api */const path = require('path')/* 控制台日志标注款式 */const chalk = require('chalk')/** * Semantic Version 版本命名标准,提供以下等性能 * 1. 比拟两个版本号的大小 * 2. 验证某个版本号是否非法 * 3. 提取版本号,例如从“=v1.2.1”体取出"1.2.1" * 4.剖析版本号是否属于某个范畴或合乎一系列条件 */const semver = require('semver')/* 获取跟目录 package.json version信息 */const currentVersion = require('../package.json').version/** * enquirer: 用户敌对、直观且易于创立的时尚CLI提醒。 * CLI(command-line interface,命令行界面)是指可在用户提示符下键入可执行指令的界面 */const { prompt } = require('enquirer')/* 用于执行内部程序 例如:git */const execa = require('execa')/** * preId * prerelease('1.2.3-alpha.1') -> ['alpha', 1] */const preId = args.preid || semver.prerelease(currentVersion)[0] || 'alpha'const isDryRun = args.dryconst skipTests = args.skipTestsconst skipBuild = args.skipBuild/** * 获取模块名称列表 * [ *    'compiler-core', *    'compiler-dom', *    'compiler-sfc', *    'compiler-ssr', *    'reactivity', *    'runtime-core', *    'runtime-dom', *    'runtime-test', *    'server-renderer', *    'shared', *    'size-check', *    'template-explorer', *    'vue' * ] */const packages = fs  .readdirSync(path.resolve(__dirname, '../packages'))  .filter(p => !p.endsWith('.ts') && !p.startsWith('.'))const skippedPackages = []const versionIncrements = [  'patch',  'minor',  'major',  'prepatch',  'preminor',  'premajor',  'prerelease']/** * inc 版本升级 * semver.inc('1.2.3', 'prerelease', 'beta') // 1.2.4-beta.0 */const inc = i => semver.inc(currentVersion, i, preId)// bin: 执行.bin下指令的 函数const bin = name => path.resolve(__dirname, '../node_modules/.bin/' + name)// run: 通过execa执行内部程序的函数封装const run = (bin, args, opts = {}) =>  execa(bin, args, { stdio: 'inherit', ...opts })// dryRun: 打印执行程序的相干信息,不会理论执行const dryRun = (bin, args, opts = {}) =>  console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)// runIfNotDry: 通过 isDryRun 变量 决定run函数const runIfNotDry = isDryRun ? dryRun : run// getPkgRoot: 获取指定模块 pkg 的具体门路const getPkgRoot = pkg => path.resolve(__dirname, '../packages/' + pkg)// step: 封装 chalk 输入日志const step = msg => console.log(chalk.cyan(msg))// 主函数即入口函数async function main() {  let targetVersion = args._[0]  if (!targetVersion) {    // no explicit version, offer suggestions    /**     * prompt 函数,它承受一个“问题”对象或一组问题对象,并返回一个蕴含用户响应的对象。     * @param questions {Array|Object}     * @returns {Promise}      ? Select release type ...      > patch (3.0.0)        minor (3.0.0)        major (3.0.0)        prepatch (3.0.1-rc.0)        preminor (3.1.0-rc.0)        premajor (4.0.0-rc.0)        prerelease (3.0.0-rc.6)        custom     */    const { release } = await prompt({      type: 'select',      name: 'release',      message: 'Select release type',      choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])    })    if (release === 'custom') {      /**       * 自定义版本,会提醒以后的package.json version 为初始值进行编辑。       √ Select release type · custom       ? Input custom version » 3.0.0-rc.5       */      targetVersion = (await prompt({        type: 'input',        name: 'version',        message: 'Input custom version',        initial: currentVersion      })).version    } else {      /**       * 'prerelease (3.0.0-rc.6)'.match(/\((.*)\)/)[1]       * 返回:'3.0.0-rc.6'       */      targetVersion = release.match(/\((.*)\)/)[1]    }  }  /**   * 验证版本有效性   * semver.valid('a.b.c') // null   * semver.valid('1.2.3') // '1.2.3'   */  if (!semver.valid(targetVersion)) {    throw new Error(`invalid target version: ${targetVersion}`)  }  /**   * 二次确认版本信息   * ? Releasing v3.0.0-rc.6. Confirm? (y/N) » false   */  const { yes } = await prompt({    type: 'confirm',    name: 'yes',    message: `Releasing v${targetVersion}. Confirm?`  })  if (!yes) {    return  }  // run tests before release  // 公布前单元测试  step('\nRunning tests...')  if (!skipTests && !isDryRun) {    await run(bin('jest'), ['--clearCache'])    await run('yarn', ['test'])  } else {    console.log(`(skipped)`)  }  // update all package versions and inter-dependencies  // 更新 package.json 版本 & 外部依赖模块版本  step('\nUpdating cross dependencies...')  updateVersions(targetVersion)  // build all packages with types  step('\nBuilding all packages...')  if (!skipBuild && !isDryRun) {    await run('yarn', ['build', '--release'])    // test generated dts files    step('\nVerifying type declarations...')    await run('yarn', ['test-dts-only'])  } else {    console.log(`(skipped)`)  }  /**   * generate changelog   * 从git元数据生成变更日志   * conventional-changelog -p angular -i CHANGELOG.md -s   */  await run(`yarn`, ['changelog'])  const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })  // 判断有批改信息,进行code提交  if (stdout) {    step('\nCommitting changes...')    await runIfNotDry('git', ['add', '-A'])    await runIfNotDry('git', ['commit', '-m', `release: v${targetVersion}`])  } else {    console.log('No changes to commit.')  }  // publish packages  step('\nPublishing packages...')  for (const pkg of packages) {    await publishPackage(pkg, targetVersion, runIfNotDry)  }  // push to GitHub  step('\nPushing to GitHub...')  await runIfNotDry('git', ['tag', `v${targetVersion}`])  await runIfNotDry('git', ['push', 'origin', `refs/tags/v${targetVersion}`])  await runIfNotDry('git', ['push'])  if (isDryRun) {    console.log(`\nDry run finished - run git diff to see package changes.`)  }  if (skippedPackages.length) {    console.log(      chalk.yellow(        `The following packages are skipped and NOT published:\n- ${skippedPackages.join(          '\n- '        )}`      )    )  }  console.log()}function updateVersions(version) {  // 1. update root package.json  updatePackage(path.resolve(__dirname, '..'), version)  // 2. update all packages  packages.forEach(p => updatePackage(getPkgRoot(p), version))}function updatePackage(pkgRoot, version) {  const pkgPath = path.resolve(pkgRoot, 'package.json')  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))  pkg.version = version  updateDeps(pkg, 'dependencies', version)  updateDeps(pkg, 'peerDependencies', version)  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')}/** * 更新外部模块依赖包版本 * "dependencies": { *   "@vue/shared": "3.0.0-rc.5", *   "@vue/compiler-dom": "3.0.0-rc.5", *   "@vue/runtime-dom": "3.0.0-rc.5" * }, */function updateDeps(pkg, depType, version) {  const deps = pkg[depType]  if (!deps) return  Object.keys(deps).forEach(dep => {    // 判断 "@vue/shared" => shared 是 packages 外部模块时再批改版本    if (      dep === 'vue' ||      (dep.startsWith('@vue') && packages.includes(dep.replace(/^@vue\//, '')))    ) {      console.log(        chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`)      )      deps[dep] = version    }  })}// 软件包公布到 npm 注册表,该注册表用于在寰球范畴内散发软件包。async function publishPackage(pkgName, version, runIfNotDry) {  if (skippedPackages.includes(pkgName)) {    return  }  const pkgRoot = getPkgRoot(pkgName)  const pkgPath = path.resolve(pkgRoot, 'package.json')  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))  if (pkg.private) {    return  }  // for now (alpha/beta phase), every package except "vue" can be published as  // `latest`, whereas "vue" will be published under the "next" tag.  const releaseTag = pkgName === 'vue' ? 'next' : null  // TODO use inferred release channel after official 3.0 release  // const releaseTag = semver.prerelease(version)[0] || null  step(`Publishing ${pkgName}...`)  try {    await runIfNotDry(      'yarn',      [        'publish',        '--new-version',        version,        ...(releaseTag ? ['--tag', releaseTag] : []),        '--access',        'public'      ],      {        cwd: pkgRoot,        stdio: 'pipe'      }    )    console.log(chalk.green(`Successfully published ${pkgName}@${version}`))  } catch (e) {    if (e.stderr.match(/previously published/)) {      console.log(chalk.red(`Skipping already published: ${pkgName}`))    } else {      throw e    }  }}main().catch(err => {  console.error(err)})

如果您对 “前端源码” 情有独钟,可微信关注前端源码解析公众号,内容继续更新中!

以后 VUE3.0 源码正在解析中,欢送捧场!

~