1. Introduction

Hi, I’m Wakawa. Welcome to follow my official account Ruochuan Vision. Recently, I organized a reading activity of source code together. If you are interested, you can add my wechat account ruochuan12 for long-term communication and learning.

Underscore, Lodash, Vuex, Sentry, Axios, Redux, KOa, VuUE-DevTools, and Vex.underscore

Writing relatively difficult source code, consuming their own time and energy, and not getting many reading likes, is actually quite a frustrating thing. In terms of reading volume and benefit to readers, it does not promote the author’s continuous output of articles.

So change your mind and write something relatively easy to understand. In fact, the source code is not so difficult to imagine, at least there are a lot of read understand.

Recently, Rainy Creek released version 3.2. The smaller version is already 3.2.4. This article to learn how to release vuejs university, learn the source code for their own use.

The vuE-next /scripts/ relex.js file mentioned in this article has only about 200 lines of code, but it is worth learning.

Goethe once said: reading a good book, is talking with noble people. The same can be said: read the source code, but also a way to learn and communicate with the author.

Reading this article, you will learn:

1. Familiar with vuejS release process 2. Learn to debug NodeJS code 3. Optimize the project release processCopy the code

Before the environment is ready, let’s preview the vuejS release process.

2. Prepare the environment

With Vue-next open, open-source projects can generally find contribution guidelines at readme. md or.github/contributing.

The contribution guide has a lot of information about participating in project development. Such as how to run, what is the project directory structure. How to put into development, what knowledge reserves need.

You need to make sure the node. js version is 10+ and the yarn version is 1.x yarn 1.x.

The node.js version you have installed is probably less than 10. The easiest way is to go to the official website and reinstall. You can also use NVM to manage node.js versions.

node -v
# v14.16.0
# Install YARN globally
# Clone project
git clone https://github.com/vuejs/vue-next.git
cd vue-next

# or clone my project
git clone https://github.com/lxchuan12/vue-next-analysis.git
cd vue-next-analysis/vue-next

# installation of yarn
npm install --global yarn
# install dependencies
yarn # install the dependencies of the project
# yarn release
Copy the code

2.1 Strict Verification Use YARN to install dependencies

Let’s take a look at the vue-next/package.json file.

// vue-next/package.json
{
    "private": true."version": "3.2.4."."workspaces": [
        "packages/*"]."scripts": {
        // -- THE dry parameter was added by me and is also recommended if you are debugging code
        // Do not test, compile, push git, etc
        // This is an example of an empty run
        "release": "node scripts/release.js --dry"."preinstall": "node ./scripts/checkYarn.js",}}Copy the code

If you try to install dependencies using NPM, you should get an error. Why did I get an error? Json has a preinstall node./scripts/ checkyarn. js to determine that the mandatory installation is yarn.

Scripts/checkyarn. js file is as follows, that is, look for the execution path npm_execpath in the process.env environment variable. If it is not YARN, print a warning and the process ends.

// scripts/checkYarn.js
if (!/yarn\.js$/.test(process.env.npm_execpath || ' ')) {
  console.warn(
    '\u001b[33mThis repository requires Yarn 1.x for scripts to work properly.\u001b[39m\n'
  )
  process.exit(1)}Copy the code

If you want to ignore this lead-in hook judgment, use yarn –ignore-scripts. There are also post-hook posts. See the NPM documentation for more details

2.2 Debug vue-next/scripts/release.js

Next we’ll learn how to debug vue-next/scripts/ relex.js.

Here I declare that my VSCode version is 1.59.0 and should start from 1.50.0. I can follow the following steps to debug.

code -v
# 1.59.0
Copy the code

Go to vuE-next /package.json and open it, and then above scripts, you’ll see the Debug button, click on it, and select Release. You can enter debugging mode.

The terminal will look like the picture below, with Debugger Attached. The output. So here’s a picture.

See the official documentation for more nodeJS debugging

Learn to debug, first roughly go through the process, in the key place to play a few breakpoints to go several times, you can guess the source code intent.

Some dependency introductions and function declarations at the beginning of the file

We can follow the breakpoints by looking at some dependency introductions and function declarations at the beginning of the file

3.1 Part I

// vue-next/scripts/release.js
const args = require('minimist')(process.argv.slice(2))
// File module
const fs = require('fs')
/ / path
const path = require('path')
/ / console
const chalk = require('chalk')
const semver = require('semver')
const currentVersion = require('.. /package.json').version
const { prompt } = require('enquirer')

// Executing a subprocess command simply means executing a command on the terminal command line
const execa = require('execa')
Copy the code

With dependencies, we can find the installed dependencies in node_modules. You can also find its README and Github repositories.

3.1.1 Minimist Command Line Parameter Parsing

minimist

The library, in short, parses command-line arguments. By looking at examples, we can easily understand the result of passing parameters and parsing.

$ node example/parse.js -a beep -b boop
{ _: [], a: 'beep', b: 'boop' }

$ node example/parse.js -x 3 -y 4 -n5 -abc --beep=boop foo bar baz
{ _: [ 'foo'.'bar'.'baz' ],
  x: 3,
  y: 4,
  n: 5,
  a: true,
  b: true,
  c: true,
  beep: 'boop' }
Copy the code
const args = require('minimist')(process.argv.slice(2))
Copy the code

The first and second elements of process.argv are the fully qualified file system paths for the Node executable and the JavaScript files being executed, whether or not you enter them as such.

3.1.2 Multi-color output of the Chalk terminal

chalk

Simply put, this is used for terminal display multi-color output.

3.1.3 Semver semantic version

semver

Nodejs implementation of semantic version, used for version verification comparison, etc. See the semantic version 2.0.0 documentation for the semantic version

Version format: Major version number. Major: when you make an incompatible API change, minor: when you make a backward-compatible functional addition, revision: when you make a backward-compatible problem fix. The prior version number and version build information can be added to the major version number. Second version number. Revision number “is followed as an extension.

3.1.4 Enquirer Interactive Query CLI

Simply put, it is interactive to ask the user for input.

enquirer

3.1.5 Execa Run the command

Simply speaking, it is to execute commands, similar to our own input commands in the terminal, such as Echo Ruochuan.

execa

/ / case
const execa = require('execa');

(async() = > {const {stdout} = await execa('echo'['unicorns']);
  console.log(stdout);
  //=> 'unicorns'}) ();Copy the code

Having finished part one, let’s move on to Part two.

3.2 Part 2

// vue-next/scripts/release.js

// Corresponding YARN Run release --preid=beta
// beta
const preId =
  args.preid ||
  (semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0])
// Corresponding YARN Run release --dry
// true
const isDryRun = args.dry
// Correspond to YARN Run release --skipTests
// true skips the test
const skipTests = args.skipTests
// Corresponding yarn Run Release --skipBuild
// true
const skipBuild = args.skipBuild

// Read packages folder, filter out not.ts file end and not. Folder at the beginning
const packages = fs
  .readdirSync(path.resolve(__dirname, '.. /packages'))
  .filter(p= >! p.endsWith('.ts') && !p.startsWith('. '))
Copy the code

Part 2 is relatively easy. Move on to Part 3.

3.3 Part 3

// vue-next/scripts/release.js

// Skip the package
const skippedPackages = []

// Version increment
const versionIncrements = [
  'patch'.'minor'.'major'. (preId ? ['prepatch'.'preminor'.'premajor'.'prerelease'] : [])]const inc = i= > semver.inc(currentVersion, i, preId)
Copy the code

This one might not be easy to understand. Inc is building a version. See the Semver documentation for more

semver.inc('3.2.4'.'prerelease'.'beta')
/ / 3.2.5 - beta. 0
Copy the code

3.4 Part 4

The fourth part declares some execution script functions and so on

// vue-next/scripts/release.js

// Get the bin command
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

// Get the package path
const getPkgRoot = pkg= > path.resolve(__dirname, '.. /packages/' + pkg)

// Console output
const step = msg= > console.log(chalk.cyan(msg))
Copy the code

3.4.1 track bin function

Get the commands in node_modules/.bin/, the entire file is used once.

bin('jest')
Copy the code

In the command terminal, run the./node_modules/. Bin /jest command in the project root directory.

3.4.2 Run, dryRun, runIfNotDry

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
Copy the code

Run Actually runs commands on the terminal, such as YARN Build –release

DryRun does not run, just console.log(); Print ‘YARN Build –release’

RunIfNotDry executes the command if it is not running empty. The isDryRun parameter is entered through the console. Yarn Run release –dry is true. RunIfNotDry simply prints and does not execute commands. The advantage of this design is that sometimes you don’t want to commit directly to see the result of executing the command first. I have to say, especially college can play.

You can see a similar prompt at the end of the main function. You can use Git diff to look at file changes first.

if (isDryRun) {
  console.log(`\nDry run finished - run git diff to see package changes.`)}Copy the code

After looking at the dependencies introduced and function declarations at the beginning of the file, let’s move on to the main main entry function.

4 Main Main process

Section 4 is mainly about the disassembly analysis of main function.

4.1 Process Combing the main function

const chalk = require('chalk')
const step = msg= > console.log(chalk.cyan(msg))
// A bunch of dependency introductions and function definitions
async function main(){
  // Version verification

  // run tests before release
  step('\nRunning tests... ')
  // update all package versions and inter-dependencies
  step('\nUpdating cross dependencies... ')
  // build all packages with types
  step('\nBuilding all packages... ')

  // generate changelog
  step('\nCommitting changes... ')

  // publish packages
  step('\nPublishing packages... ')

  // push to GitHub
  step('\nPushing to GitHub... ')
}

main().catch(err= > {
  console.error(err)
})
Copy the code

The main function above omits much of the concrete function implementation. Next we break down the main function.

4.2 Confirm the version to be released

The first piece of code is longer, but easy to understand. The main thing is to confirm the version to be released.

When debugging, let’s take a look at two screenshots of this section to make sense.

Yarn Run release 3.2.4
// Take the 3.2.4 parameter
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'])})// Select custom
  if (release === 'custom') {
    targetVersion = (
      await prompt({
        type: 'input'.name: 'version'.message: 'Input custom version'.initial: currentVersion
      })
    ).version
  } else {
    // Get the version number in parentheses
    targetVersion = release.match(/ / / ((. *) \)) [1]}}// Verify that the version conforms to the specification
if(! semver.valid(targetVersion)) {throw new Error(`invalid target version: ${targetVersion}`)}// Confirm to release
const { yes } = await prompt({
  type: 'confirm'.name: 'yes'.message: `Releasing v${targetVersion}. Confirm? `
})

// false returns directly
if(! yes) {return
}
Copy the code

4.3 Executing Test Cases

// 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

4.4 Update the version numbers of all packages and internal VUE dependent versions

This part is to update the version numbers of package.json and all packages in the root directory.

// update all package versions and inter-dependencies
step('\nUpdating cross dependencies... ')
updateVersions(targetVersion)
Copy the code
function updateVersions(version) {
  // 1. update root package.json
  updatePackage(path.resolve(__dirname, '.. '), version)
  // 2. update all packages
  packages.forEach(p= > updatePackage(getPkgRoot(p), version))
}
Copy the code

4.4.1 updatePackage Updates the package 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')}Copy the code

There are three main modifications.

Json version of package.json. 2. Change dependencies related to Vue in Dependencies in package.json. 3 Related dependency modificationsCopy the code

A picture is worth a thousand words. Git diff check git changes after yarn Release –dry. Some screenshots are shown below.

4.4.2 updateDeps Updates the version numbers of internal VUE dependencies

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

A picture is worth a thousand words. We execute YARN Release –dry on the terminal. You’ll see that this is the output.

That’s what this code says.

console.log(
  chalk.yellow(`${pkg.name} -> ${depType} -> ${dep}@${version}`))Copy the code

4.5 Packaging Compiles all packages

// 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

4.6 generate changelog

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

Yarn Changelog corresponds to the conventional- Changelog -p angular-i Changelog.md-s script.

4.7 Submitting code

Git diff git diff git diff git diff Whether there are file changes and if there are commits.

git add -A git commit -m 'release: v${targetVersion}'

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

4.8 publish a package

// publish packages
step('\nPublishing packages... ')
for (const pkg of packages) {
  await publishPackage(pkg, targetVersion, runIfNotDry)
}
Copy the code

Yarn publish publish package this is a long function. After we yarn release –dry, the terminal output of this function is as follows:

It is worth mentioning that vue has a tag next by default. Deleted when Vue 3.x is the default.

} else if (pkgName === 'vue') {
  // TODO remove when 3.x becomes default
  releaseTag = 'next'
}
Copy the code

That’s why we’re still installing vue3 with the NPM I vue@next command.

async function publishPackage(pkgName, version, runIfNotDry) {
  // Skip if in skip package
  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, all 3.x packages except "vue" can be published as
  // `latest`, whereas "vue" will be published under the "next" tag.
  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'
  } else if (pkgName === 'vue') {
    // TODO remove when 3.x becomes default
    releaseTag = 'next'
  }

  // TODO use demographics 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'] and {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
    }
  }
}
Copy the code

4.9 Push to Github

// push to GitHub
step('\nPushing to GitHub... ')
/ / to play tag
await runIfNotDry('git'['tag'.`v${targetVersion}`])
/ / push the tag
await runIfNotDry('git'['push'.'origin'.`refs/tags/v${targetVersion}`])
// git push all changes to remote github
await runIfNotDry('git'['push'])
Copy the code
// yarn run release --dry

Git diff: git diff: git diff: git diff: git diff: git diff

// const isDryRun = args.dry
if (isDryRun) {
  console.log(`\nDry run finished - run git diff to see package changes.`)}// If packages are skipped, the following packages are not published. But there are no packages in 'skippedPackages'.
// So this code will not execute either.
// We like to write arr. Length! == 0, 0 is false. I don't have to write it.
if (skippedPackages.length) {
  console.log(
    chalk.yellow(
      `The following packages are skipped and NOT published:\n- ${skippedPackages.join(
        '\n- '
      )}`))}console.log()
Copy the code

After we yarn release –dry, the terminal output of this function is as follows:

So we’re done with the main function.

The process is very clear.

Check the version to be released 2. Perform test cases 3. Update the version numbers of all packages and internal VUE dependencies 3.1 updatePackage Update the version numbers of the packages 3.2 updateDeps Update the version numbers of internal VUE dependencies 4. Compile all packages 5. Generate Changelog 6. Submit code 7. Release package 8. Push to GithubCopy the code

To sum it up with a picture:

After reading vue-next/scripts/ relex.js, you can also look at other code in vue-next/scripts, which is relatively small in number of lines, but more profitable.

5. To summarize

Through the study of this article, we have learned these.

1. Familiar with vuejS release process 2. Learn to debug NodeJS code 3. Optimize the project release processCopy the code

At the same time, it is recommended to use VSCode debugging, perform several times in the terminal, more understanding and digestion.

Vuejs releases a lot of code that we can just copy and paste and modify to optimize our own release process. For example, write small programs, relatively likely to be released frequently, you can use this code, with miniprogram-CI, plus some customization, to optimize.

You can also use open source release-it.

At the same time, we can:

Introduce Git flow and manage git branches. Git flow is supported by Windows Git bash by default.

Introduce husky and Lint-staged commits using ESLint and other code to verify that they pass detection.

Introduce unit test JEST, test key tool functions, etc.

Introduce the but – changelog

Introduce git-cz interactive Git commit.

And so on to standardize the process of their own projects. If a candidate, by looking at vuejs published source code, actively optimize their own project. I think the interviewers will see this candidate as a plus.

The benefits of reading the source code of open source projects are: on the one hand, you can expand your horizons, on the other hand, you can use it for yourself, and the income is relatively high.


about

Author: Often in the name of ruochuan mixed traces in rivers and lakes. The front road lovers | | PPT know little, only good study. Public number ruochuan vision, dedicated to help 5 years to grow the front end of ruochuan blog segmentfault ruochuan vision column, launched a ruochuan vision column, welcome to follow ~ dig gold column, welcome to follow ~ github blog, begged a star^_^~