This is the fourth day of my participation in the August Text Challenge.More challenges in August

First of all, thank you for the reading of the source code

preface

The main purpose of this article is to learn the vuE-Next release process. After studying this article, you will gain three skills

1. Nodejs debugging skills 2. Know vuE-Next release process 3. Improve the release process for your own projectsCopy the code

The preparatory work

Clone project to local vue – next, and ensure that the node version 10 + yarn 1 x, the source code in/vue – next/blob/master/scripts/release.

After open the project, in the node can see release is executed in the scripts/vue – next/blob/master/scripts/the js

How to debug nodeJS can see the previous article you may not know vue-devTools open file principle

Release process – A picture is worth a thousand words

Source code parsing – line by line analysis

Lines 1-30, mainly introducing some packages and declaring some variables.

Minimist parsing parameters

Semvernpm version management tool

Enquirer interactive command line tool

Preid releases a good version, if there is use custom, if not use semver management.

IsDryRun Is in debugging state

Packages Packages that need to be packaged

SkippedPackages need to be skipped

VersionIncrements Which method to modify the version number

const args = require('minimist')(process.argv.slice(2))
const fs = require('fs')
const path = require('path')
const chalk = require('chalk')
const semver = require('semver')
// The semantic versioner for npm
const currentVersion = require('.. /package.json').version
const { prompt } = require('enquirer')
/ / interaction
const execa = require('execa')

const preId =
  args.preid ||
  (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
const isDryRun = args.dry
const skipTests = args.skipTests
const skipBuild = args.skipBuild
const packages = fs
  .readdirSync(path.resolve(__dirname, '.. /packages'))
  .filter(p= >! p.endsWith('.ts') && !p.startsWith('. '))

const skippedPackages = []

const versionIncrements = [
  'patch'.'minor'.'major'. (preId ? ['prepatch'.'preminor'.'premajor'.'prerelease'] : [])]Copy the code

Lines 32-40 some tool methods

Inc accepts an additional identifier string argument that takes the value of the additional string as a pre-release identifier for example:

Inc (' 1.2.3 ', 'prerelease', 'beta') / / '1 - beta. 0'Copy the code

Bin Command execution

The run execa perform

```js const inc = i => semver.inc(currentVersion, i, preId) const bin = name => path.resolve(__dirname, '.. /node_modules/.bin/' + name) const run = (bin, args, opts = {}) => execa(bin, args, { stdio: 'inherit', ... opts }) const dryRun = (bin, args, opts = {}) => console.log(chalk.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts) const runIfNotDry = isDryRun ? dryRun : run const getPkgRoot = pkg => path.resolve(__dirname, '.. /packages/' + pkg) const step = msg => console.log(chalk.cyan(msg))Copy the code

The Main function

43-80 What version do you choose to release

  let targetVersion = args._[0]

  if(! targetVersion) {// no explicit version, offer suggestions
    const { release } = await prompt({
      type: 'select'.name: 'release'.message: 'Select release type'.choices: versionIncrements.map(i= > `${i} (${inc(i)}) `).concat(['custom'])})if (release === 'custom') {
      targetVersion = (
        await prompt({
          type: 'input'.name: 'version'.message: 'Input custom version'.initial: currentVersion
        })
      ).version
    } else {
      targetVersion = release.match(/ / / ((. *) \)) [1]}}if(! semver.valid(targetVersion)) {throw new Error(`invalid target version: ${targetVersion}`)}const { yes } = await prompt({
    type: 'confirm'.name: 'yes'.message: `Releasing v${targetVersion}. Confirm? `
  })

  if(! yes) {return
  }
Copy the code

TargetVersion targetVersion

NPM run Release 1.1.2 args._ => [1.1.2]Copy the code

For details, see Minimist

If there is no target version then an interaction is triggered as follows:

VersionIncrements is defined above, if custom custom version number is selected, then the input continues:

.

After the input, Semver verifies whether the version number meets the requirements of NPM.

If it passes, it will start asking for confirmation, whether to publish.

Running tests...

81-90 Perform tests

  // run tests before release
  step('\nRunning tests... ')
  if(! skipTests && ! isDryRun) {await run(bin('jest'),'--clearCache'])
    await run('yarn'['test'.'--bail'])}else {
    console.log(`(skipped)`)}Copy the code

This passage is very simple. Whether to skip tests or just print debugging.

The run function

const run = (bin, args, opts = {}) = >
  execa(bin, args, { stdio: 'inherit'. opts })Copy the code

Run (bin(‘jest’), [‘–clearCache’]) => run jest –clearCache on the command line.

Run (‘yarn’, [‘test’, ‘– Bail ‘]) => Equivalent to YARN test — Bail. Yarn is pre-installed, so bin is not required.

91-94 Updated version number

  // update all package versions and inter-dependencies
  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)`)}Copy the code

This section starts by updating the version numbers for Packages and internals. If update, then we need to figure out updateVersions

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')}function updateDeps(pkg, depType, version) {
  const deps = pkg[depType]
  if(! deps)return
  Object.keys(deps).forEach(dep= > {
    if (
      dep === 'vue' ||
      (dep.startsWith('@vue') && packages.includes(dep.replace(/^@vue\//.' ')))) {console.log(
        chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`)
      )
      deps[dep] = version
    }
  })
}
Copy the code

UpdateVersions, updatePackage, updateDeps

UpdateVersions: Set a breakpoint in packages. ForEach (p => updatePackage(getPkgRoot(p), version)).

This is when we see that Packages are the following packages and then iterate through the updatePackage.

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')}Copy the code

Update the root version of dependencies, and then update the versions of dependencies, peerDependencies. The diagram below:

94-104 started to build

// 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)`)}Copy the code

Build Two commands are run yarn build –release YARN test-dts-only

106-116 Generate and submit a log

  // generate changelog
  await run(`yarn`['changelog'])

  const { stdout } = await run('git'['diff'] and {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.')}Copy the code

Yarn Changelog is executed in this part

Then execute git diff to commit if any changes are made. Commit comment is: release: VXXX.

123-142 line tag&push

// 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- '
        )}`))}Copy the code

This part contains simple tag, then push, and finally if there is a skipped package, the yellow font will be printed. After the release of the end ~

conclusion

By looking at the source code above, you can learn what the release process of a good open source project looks like.

There are a number of tools available to assist publishing. For example:

  • Commit from HusKY and Lint-staged commits using ESLint and others to verify that the commit will pass detection. If it does not pass detection, the commit will not succeed, thus standardizes team coding

  • Use Git-CZ to standardize the submission format and so on