Vue 團隊核心成員開發的 39 行小工具 install-pkg 安裝包,值得一學!

  1. 前言

大家好,我是若川。最近組織了源碼共讀活動,感興趣的可以點此加我微信 ruochuan12 參與,每週大家一起學習 200 行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含 20 餘篇源碼文章。

本文倉庫 https://github.com/lxchuan12/install-pkg-analysis.git,求個 star^_^[1]

源碼共讀活動 每週一期,已進行到 16 期。Vue 團隊核心成員 Anthony Fu 開發的 install-pkg[2] 小工具,主文件源碼僅 39 行,非常值得我們學習。

閱讀本文,你將學到:

1. 如何學習調試源碼
2. 如何開發構建一個 ts 的 npm 包
3. 如何配置 github action
4. 配置屬於自己的 eslint 預設、提升版本號等
5. 學會使用 execa 執行命令
6. 等等
  1. install-pkg 是什麼

Install package programmatically. Detect package managers automatically (npm, yarn and pnpm).

以編程方式安裝包。自動檢測包管理器(npmyarnpnpm)。

npm i @antfu/install-pkg
import { installPackage } from '@antfu/install-pkg'
await installPackage('vite'{ silent: true })

我們看看 npmjs.com @antfu/install-pkg[3] 有哪些包依賴的這個包。

我們可以發現目前只有以下這兩個項目使用了。

unplugin-icons[4]@chenyueban/lint[5]

我們克隆項目來看源碼。

3 克隆項目

# 推薦克隆我的項目,保證與文章同步
git clone https://github.com/lxchuan12/install-pkg-analysis.git
# npm i -g pnpm
cd install-pkg-analysis/install-pkg && pnpm i
# VSCode 直接打開當前項目
# code .

# 或者克隆官方項目
git clone https://github.com/antfu/install-pkg.git
# npm i -g pnpm
cd install-pkg && pnpm i
# VSCode 直接打開當前項目
# code .

看源碼一般先看 package.json,再看 script

{
    "name""@antfu/install-pkg",
    "version""0.1.0",
    "scripts"{
      "start""esno src/index.ts"
    },
}

關於調試可以看我的這篇文章:新手向:前端程序員必學基本技能——調試 JS 代碼,這裏就不再贅述了。

我們可以得知入口文件是 src/index.ts

src文件夾下有三個文件

src
- detect.ts
- index.ts
- install

接着我們看這些文件源碼。

  1. 源碼

4.1 index.js

導出所有

// src/install.ts
export * from './detect'
export * from './install'

我們來看 install.ts 文件,installPackage 方法。

4.2 installPackage 安裝包

// src/install.ts
import execa from 'execa'
import { detectPackageManager } from '.'

export interface InstallPackageOptions {
  cwd?: string
  dev?: boolean
  silent?: boolean
  packageManager?: string
  preferOffline?: boolean
  additionalArgs?: string[]
}

export async function installPackage(names: string | string[], options: InstallPackageOptions = {}) {
  const agent = options.packageManager || await detectPackageManager(options.cwd) || 'npm'
  if (!Array.isArray(names))
    names = [names]

  const args = options.additionalArgs || []

  if (options.preferOffline)
    args.unshift('--prefer-offline')

  return execa(
    agent,
    [
      agent === 'yarn'
        ? 'add'
        : 'install',
      options.dev ? '-D' : '',
      ...args,
      ...names,
    ].filter(Boolean),
    {
      stdio: options.silent ? 'ignore' : 'inherit',
      cwd: options.cwd,
    },
  )
}

支持安裝多個,也支持指定包管理器,支持額外的參數。

其中 github execa[6] 是執行腳本

Process execution for humans

也就是說:最終執行類似以下的腳本。

pnpm install -D --prefer-offine release-it react antd

我們接着來看 detect.ts文件 探測包管理器 detectPackageManager 函數如何實現的。

4.3 detectPackageManager 探測包管理器

根據當前目錄鎖文件,探測包管理器。

// src/detect.ts
import path from 'path'
import findUp from 'find-up'

export type PackageManager = 'pnpm' | 'yarn' | 'npm'

const LOCKS: Record<string, PackageManager> = {
  'pnpm-lock.yaml''pnpm',
  'yarn.lock''yarn',
  'package-lock.json''npm',
}

export async function detectPackageManager(cwd = process.cwd()) {
  const result = await findUp(Object.keys(LOCKS){ cwd })
  const agent = (result ? LOCKS[path.basename(result)] : null)
  return agent
}

其中 find-up[7] 查找路徑。

/
└── Users
    └── install-pkg
        ├── pnpm-lock.yaml
import {findUp} from 'find-up';

console.log(await findUp('pnpm-lock.yaml'));
//='/Users/install-pkg/pnpm-lock.yaml'

path.basename('/Users/install-pkg/pnpm-lock.yaml') 則是 pnpm-lock.yaml

所以在有pnpm-lock.yaml文件的項目中,detectPackageManager 函數最終返回的是 pnpm

至此我們可以用一句話總結原理就是:通過鎖文件自動檢測使用何種包管理器(npm、yarn、pnpm),最終用 execa[8] 執行類似如下的命令。

pnpm install -D --prefer-offine release-it react antd

看完源碼,我們接着來解釋下 package.json 中的 scripts 命令。

  1. package.json script 命令解析

{
    "name""@antfu/install-pkg",
    "version""0.1.0",
    "scripts"{
      "prepublishOnly""nr build",
      "dev""nr build --watch",
      "start""esno src/index.ts",
      "build""tsup src/index.ts --format cjs,esm --dts --no-splitting",
      "release""bumpp --commit --push --tag && pnpm publish",
      "lint""eslint \"{src,test}/**/*.ts\"",
      "lint:fix""nr lint -- --fix"
    },
}

5.1 ni 神器

github ni[9]

我之前寫過源碼文章。

尤雨溪推薦神器 ni ,能替代 npm/yarn/pnpm ?簡單好用!源碼揭祕![10]

自動根據鎖文件 yarn.lock / pnpm-lock.yaml / package-lock.json 檢測使用 yarn / pnpm / npm 的包管理器。

nr dev --port=3000

# npm run dev -- --port=3000
# yarn run dev --port=3000
# pnpm run dev -- --port=3000
nr
# 交互式選擇腳本
# interactively select the script to run
# supports https://www.npmjs.com/package/npm-scripts-info convention

nci - clean install

nci
# npm ci
# 簡單說就是不更新鎖文件
# yarn install --frozen-lockfile
# pnpm install --frozen-lockfile

pnpm install --frozen-lockfile[11]

5.2 esno 運行 ts

esno[12]

TypeScript / ESNext node runtime powered by esbuild

源碼也不是很多。

#!/usr/bin/env node

const spawn = require('cross-spawn')
const spawnSync = spawn.sync

const register = require.resolve('esbuild-register')

const argv = process.argv.slice(2)

process.exit(spawnSync('node'['-r', register, ...argv]{ stdio: 'inherit' }).status)

esbuild-register[13] 簡單說:使用 esbuild 即時傳輸 JSX、TypeScript 和 esnext 功能

5.3 tsup 打包 ts

打包 TypeScript 庫的最簡單、最快的方法。

tsup[14]

5.4 bumpp 交互式提升版本號

bumpp[15]

version-bump-prompt[16]

交互式 CLI 可增加您的版本號等

5.5 eslint 預設

eslint 預設 [17]

pnpm add -D eslint @antfu/eslint-config

添加 .eslintrc 文件

// .eslintrc
{
  "extends"["@antfu"],
  "rules"{}
}

之前看其他源碼發現的也很好用的 eslint 工具 xo

xo[18]

JavaScript/TypeScript linter (ESLint wrapper) with great defaults JavaScript/TypeScript linter(ESLint 包裝器)具有很好的默認值

看完 scripts 命令解析,我們來看看 github action 配置。

  1. github action workflows

對於 github action 不熟悉的讀者,可以看阮一峯老師 GitHub Actions 入門教程 [19]

配置文件 workflows/release[20]

構建歷史 github action workflow[21]

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
          registry-url: https://registry.npmjs.org/
      - run: npm i -g pnpm @antfu/ni
      - run: nci
      - run: nr test --if-present
      - run: npx conventional-github-releaser -p angular
        env:
          CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{secrets.GITHUB_TOKEN}}

根據每次 tags 推送,執行。

# 全局安裝 pnpm 和 ni
npm i -g pnpm @antfu/ni
# 如何存在 test 命令則執行
nr test --if-present

nci - clean install

nci
# npm ci
# 簡單說就是不更新鎖文件
# yarn install --frozen-lockfile
# pnpm install --frozen-lockfile

最後 npx conventional-github-releaser -p angularconventional-github-releaser[22]

生成 changelog

至此我們就學習完了 install-pkg[23] 包。

  1. 總結

整體代碼比較簡單。原理就是通過鎖文件自動檢測使用何種包管理器(npm、yarn、pnpm),最終用 execa[24] 執行類似如下的命令。

pnpm install -D --prefer-offine release-it react antd

我們學到了:

1. 如何學習調試源碼
2. 如何開發構建一個 ts 的 npm 包
3. 如何配置 github action
4. 配置屬於自己的 eslint 預設、提升版本號等
5. 學會使用 execa 執行命令
6. 等等

還有各種依賴工具。

建議讀者克隆 我的倉庫 [25] 動手實踐調試源碼學習。

最後可以持續關注我 @若川。歡迎加我微信 ruochuan12 交流,參與 源碼共讀 活動,每週大家一起學習 200 行左右的源碼,共同進步。

參考資料

[1]

本文倉庫 https://github.com/lxchuan12/install-pkg-analysis.git,求個 star^_^: https://github.com/lxchuan12/install-pkg-analysis.git
更多資料點擊閱讀原文查看

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ocu_s2cy9FjlUELmsUSe1A