npm-modules

pkg-install

以编程方式安装包。自动检测包管理器(npm、yarn 和 pnpm)

目标

这个包不大,知识点扩展开来涉及不少。

作用

自动检测包管理器(npm、yarn 和 pnpm)

import { installPackage } from '@antfu/install-pkg'

await installPackage('vite', { silent: true })

原理就是通过锁文件自动检测使用何种包管理器(npm、yarn、pnpm),最终用 execa 执行类似如下的命令。

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

源码分析

index.ts 入口文件

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

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
}

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,
    },
  )
}

依赖包 find-up 用于查找路径

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

package.json scripts 解析

"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"
},

ni 神器

自动根据锁文件 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

esno 运行 ts

#!/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)

tsup 打包 ts

打包 TypeScript 库的最简单、最快的方法。

bumpp 交互式提升版本号

bumpp Forked from version-bump-prompt

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

eslint 预设

pnpm add -D eslint @antfu/eslint-config

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

github action workflows

知识点