美文网首页
2023.21 vue 3.x 是怎么发布的 vue-relea

2023.21 vue 3.x 是怎么发布的 vue-relea

作者: wo不是黄蓉 | 来源:发表于2023-05-06 13:28 被阅读0次
    vue 3.2 是怎么发布的 vue-release.png

    大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。

    本文有感于若川的源码共读第3期。

    上篇文章写到想学习一下vueparsecreateTemplate方法,由于看了源码之后,感觉parse方法没什么好讲的,大致流程就是挨个读取vue文件内容,使用正则匹配标签,设置游标,每次匹配到标签后进行解析,关键函数parseElement、parseTag等,解析完成后将节点存入nodes这个数组中,这个数组对象中存的信息包含节点类型、子节点、属性集合标签名等,每解析一步游标就往前推进一步,直至解析结束。

    进入正题

    首先clone项目

    查看package.json找到发布相关脚本

    
      "scripts": {
        "release": "node scripts/release.js",
      },
    

    找到入口函数

    
    main().catch(err => {
      updateVersions(currentVersion)
      console.error(err)
      process.exit(1)
    })
    

    第1步:main函数

    如果执行pnpm run release时带了参数,就指定自定义的版本号,接着执行main的回调函数,更新版本号流程结束。

    如果没有:

    1.1借助prompt实现交互提示选择打包的版本类型,patch为打补丁的包,minor为子包,major为主包,相关版本号命名规范可以参考

    1683364715528.png
    async function main() {
      let targetVersion = args._[0]
      if (!targetVersion) {
          //使用prompt做输入提示
        const { release } = await prompt({
          type: 'select',
          name: 'release',
          message: 'Select release type',
          choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])
        })
    
        if (release === 'custom') {
          const result = await prompt({
            type: 'input',
            name: 'version',
            message: 'Input custom version',
            initial: currentVersion
          })
          // @ts-ignore
          targetVersion = result.version
        } else {
          targetVersion = release.match(/((.*))/)[1]
        }
      }
    
      if (skipPrompts) {
        step(
          isCanary
            ? `Releasing canary version v${targetVersion}...`
            : `Releasing v${targetVersion}...`
        )
      } else {
        // @ts-ignore
        const { yes: confirmRelease } = await prompt({
          type: 'confirm',
          name: 'yes',
          message: `Releasing v${targetVersion}. Confirm?`
        })
    
        if (!confirmRelease) {
          return
        }
      }
    }
    

    第2步 是否跳过执行测试用例

    
      if (!skipTests) {
        step('Checking CI status for HEAD...')
        let isCIPassed = await getCIResult()
        skipTests ||= isCIPassed
    
        if (isCIPassed && !skipPrompts) {
          // @ts-ignore
          const { yes: promptSkipTests } = await prompt({
            type: 'confirm',
            name: 'yes',
            message: `CI for this commit passed. Skip local tests?`
          })
    
          skipTests = promptSkipTests
        }
      }
    

    execa执行命令git rev-parse显示.git所在位置

    git rev-parse是干啥用的?

    找了很多资料都说的是用于操作的辅助管道命令,不理解这句话啥意思,但是执行git rev-parse HEAD输出的最近一次提交的commitid

    关于git api的这个请求返回的内容,可以参考,这个地方获取的信息应该是需要确认你这次发布的内容中是否有新的提交信息,如果有的话才执行提示命令是否继续执行用例

    async function getCIResult() {
      try {
          //
        const { stdout: sha } = await execa('git', ['rev-parse', 'HEAD'])
        //列出所有仓库关联的分支,加上参数head_sha表示,只返回与指定head_sha关联的工作流运行,sha也就是最近一次的commitid,也就是返回最近一次提交的所有工作流运行
        const res = await fetch(
          `https://api.github.com/repos/vuejs/core/actions/runs?head_sha=${sha}` +
            `&status=success&exclude_pull_requests=true`
        )
        const data = await res.json()
        return data.workflow_runs.length > 0
      } catch (e) {
        return false
      }
    }
    
    

    1.2.1 run方法,这个方法只是对execa方法进行了封装,也是为了执行命令用的,具体执行什么命令要看传参

    
    const run = (bin, args, opts = {}) =>
      execa(bin, args, { stdio: 'inherit', ...opts })
    
    //main函数
    if (!skipTests) {
        step('\nRunning tests...')
        if (!isDryRun) {
          await run('pnpm', ['test', 'run'])
        } else {
          console.log(`Skipped (dry run)`)
        }
      } else {
        step('Tests skipped.')
      }
    

    第3步 更新版本号

    updateVersions更新版本号,更新packages中所有子包的版本号,更新主包里面依赖的版本号

    
    function updateVersions(version, getNewPackageName = keepThePackageName) {
      // 1. update root package.json
      updatePackage(path.resolve(__dirname, '..'), version, getNewPackageName)
      // 2. update all packages
      packages.forEach(p =>
        updatePackage(getPkgRoot(p), version, getNewPackageName)
      )
    }
    

    第4步 执行打包命令

    pnpm run build --withTypes

    pnpm run test-dts-only

    
    //main函数
      if (!skipBuild && !isDryRun) {
        await run('pnpm', ['run', 'build', '--withTypes'])
        step('\nTesting built types...')
        await run('pnpm', ['test-dts-only'])
      } else {
        console.log(`(skipped)`)
      }
    

    第5步 生成更新日志

    pnpm run changelog

    
    //main函数  
    step('\nGenerating changelog...')
      await run(`pnpm`, ['run', 'changelog'])
    

    第6步 git操作

    对比两个版本内容git diff

    如果有没有提交的内容执行git add -A git commit -m 'release:v 版本号'

    
    //main函数 
    if (!skipGit) {
        const { stdout } = await run('git', ['diff'], { stdio: 'pipe' })
        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.')
        }
      }
    

    第7步 发布包,主要是发布成功时打印提示信息

    需要查看发布包的类型,根据发布包的类型生成发布提示信息。如果一开始执行pnpm run release 3.3.0-beta.4时指定了版本号,则上面步骤可以省略,直接执行到main函数的回调函数,执行updateVersions操作

    async function publishPackage(pkgName, version) {
      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
      }
    
      let releaseTag = null
      if (args.tag) {
        releaseTag = args.tag
      } else if (version.includes('alpha')) {
        releaseTag = 'alpha'
      } else if (version.includes('beta')) {
        releaseTag = 'beta'
      } else if (version.includes('rc')) {
        releaseTag = 'rc'
      }
    
      step(`Publishing ${pkgName}...`)
      try {
        await run(
          // note: use of yarn is intentional here as we rely on its publishing
          // behavior.
          'npm',
          [
            'publish',
            ...(releaseTag ? ['--tag', releaseTag] : []),
            '--access',
            'public',
            ...(isDryRun ? ['--dry-run'] : [])
          ],
          {
            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
        }
      }
    }
    

    至此,vue发布时的流程已经梳理清楚,代码没有那么复杂,但是可以学一些思路。

    从这里面我们可以学到如果我们想做一些用户交互性的内容,就可以借助node+enquirer实现用户交互,脚手架相关原理应该也是如此,根据对应的选项做不同的操作。

    下节学习代码规范相关内容。

    相关文章

      网友评论

          本文标题:2023.21 vue 3.x 是怎么发布的 vue-relea

          本文链接:https://www.haomeiwen.com/subject/kodcsdtx.html