Skip to content

使用father工具开发并发布npm包

前言

在开发过程中,为了代码的可复用性,我们会将一些公共的代码抽离出来,形成一个独立的包,然后在项目中使用。但是如果自己搭建一个npm包,需要自己维护版本号,发布包,然后在项目中使用,这会比较麻烦。也有一篇文章介绍了如何不用工具发布npm包。这里试试这个发包的工具如何发布并维护包

简介

father 是一款 NPM 包研发工具,能够帮助开发者更高效、高质量地研发 NPM 包、生成构建产物、再完成发布。它主要具备以下特性: 文档 ⚔️ 双模式构建: 支持 Bundless 及 Bundle 两种构建模式,ESModule 及 CommonJS 产物使用 Bundless 模式,UMD 产物使用 Bundle 模式 🎛 多构建核心: Bundle 模式使用 Webpack 作为构建核心,Bundless 模式支持 esbuild、Babel 及 SWC 三种构建核心,可通过配置自由切换 🔖 类型生成: 无论是源码构建还是依赖预打包,都支持为 TypeScript 模块生成 .d.ts 类型定义 🚀 持久缓存: 所有产物类型均支持持久缓存,二次构建或增量构建只需『嗖』的一下 🩺 项目体检: 对 NPM 包研发常见误区做检查,让每一次发布都更加稳健 🏗 微生成器: 为项目追加生成常见的工程化能力,例如使用 jest 编写测试 📦 依赖预打包: 开箱即用的依赖预打包能力,帮助 Node.js 框架/库提升稳定性、不受上游依赖更新影响(实验性)

新建一个father项目

bash
npx create-father my-father-project
// or
pnpm dlx create-father my-father-project

init 初始化的时候会让你选择这个包的使用范围,是浏览器还是node,还是都支持。这里试试都支持 image 看下文件目录

bash
> tree -L 3 -I 'node_modules|.git|dist'
.
├── README.md
├── package.json
├── pnpm-lock.yaml
├── src
│   ├── client
│   │   └── index.ts
│   └── server
│       └── index.ts
└── tsconfig.json

3 directories, 6 files

构建方式

father 支持两种构建方式:

  • Bundless Bundless 即文件到文件的构建模式,它不对依赖做任何处理,只对源码做平行编译输出。目前社区里的 tsc、unbuild 及旧版 father 的 babel 模式都是对源码做 Bundless 构建的构建工具。在 father 4 中,输出 ESModule 产物和 CommonJS 产物时都会采用 Bundless 构建模式
.
└── src
    ├── index.less
    ├── index.tsx
    └── util.js

配合以下构建配置:

export default {
  esm: { output: 'dist' },
  // 或者
  cjs: { output: 'dist' },
};

则会被 father 输出为:

.
└── dist
    ├── index.d.ts
    ├── index.js
    ├── index.less
    └── util.js

可以发现,在 Bundless 模式下,father 对源码的处理逻辑如下:

TypeScript 模块会被编译为 JavaScript 模块,并且输出对应的 .d.ts 类型文件 JavaScript 模块会被编译为 JavaScript 模块,做兼容性处理 其他模块会被直接拷贝输出,不做编译

  • Bundle Bundle 即将源码打包的构建模式,它以入口文件作为起点、递归处理全部的依赖,然后将它们合并输出成构建产物。目前社区里的 Webpack、Rollup 及旧版 father 的 rollup 模式都是对源码做 Bundle 构建的构建工具。

在 father 4 中,仅输出 UMD 产物时会使用 Bundle 构建模式。来看一下 father 的 Bundle 构建模式会如何工作。

.
└── src
    ├── index.less
    └── index.tsx # 源码中引入 index.less

配合以下构建配置:

export default {
  umd: { output: 'dist' },
};

则会被 father 输出为:

.
└── dist
    ├── index.min.js
    └── index.min.css

可以发现,在 Bundle 模式下,father 会对源码进行打包,最后输出压缩后的 JavaScript 产物和 CSS 产物。

构建产物

在father中,构建产物分有3种,分别是

  • ESModule 是 JavaScript 使用的模块规范
  • CommonJS 是 Node.js 使用的模块规范
  • UMD 只有在满足如下任意条件的情况下,才需要选择输出 UMD 产物:
  1. 项目的用户可能需要将该依赖做 external 处理、并在 HTML 中通过 script 标签直接引入 CDN 上的产物(类似 React 或 antd)
  2. 项目需要产出编译后的样式表给用户使用,例如将 Less 文件以特定的变量编译成 CSS 文件,常见于基于 antd、又需要自定义主题的组件库

配置

在father打包的项目中,会有一个.fatherrc.ts的配置文件,在里面可以配置一些打包的配置,比如打包的模式,打包的输出路径等。一些常用的配置:

公共配置

alias

  • 类型:Record<string, string>
  • 默认值:undefined

指定源码编译/转换过程中需要处理的别名,其中 Bundles 模式会自动将 .js.d.ts 产物中本地路径的别名转换为相对路径。

define

  • 类型:Record<string, string>
  • 默认值:undefined

指定源码编译/转换过程中需要替换的变量,用法与 Webpack DefinePlugin 一致。

extends

  • 类型:string
  • 默认值:undefined

指定继承的父配置文件路径。

extraBabelPlugins

  • 类型:string[]
  • 默认值:undefined

指定要额外挂载的 babel 插件。

注:在 Bundless 模式下、且 transformeresbuildswc 时,该配置不生效。

extraBabelPresets

  • 类型:string[]
  • 默认值:undefined

指定要额外挂载的 babel 插件集。

注:在 Bundless 模式下、且 transformeresbuildswc 时,该配置不生效。

platform

  • 类型:browser | node
  • 默认值:<auto>

指定构建产物的目标平台,其中 esmumd 产物的默认 platformbrowsercjs 产物的默认 platformnode;指定为 browser 时产物默认兼容至 IE11,指定为 node 时产物默认兼容至 Node.js v14。

注:Bundless 模式下,如果手动将 transformer 指定为 esbuild,那么 browser 产物默认兼容性为 Chrome51 而不是 IE11。

sourcemap

  • 类型:boolean
  • 默认值:false

为 JavaScript 构建产物生成 sourcemap 文件。

注:Bundless 模式下 map 对象的 file 字段为空

targets

  • 类型: Record<string, number>
  • 默认值:<auto>

指定源码编译产物的兼容性,不同目标平台和编译模式下的默认值如下:

platformtransformerdefault value
browserbabel{ ie: 11 }
browseresbuild{ chrome: 51 }
browserswc{ ie: 11 }
nodebabel{ node: 14 }
nodeesbuild{ node: 14 }
nodeswc{ node: 14 }

构建配置

father 以构建产物类型划分构建配置,其中 esmcjs 产物为 Bundless 构建模式,umd 产物为 Bundle 构建模式,另外依赖预打包 prebundle 产物也为 Bundle 构建模式。

esm/cjs

  • 类型:object
  • 默认值:undefined

配置将源码转换为 ESModule/CommonJS 产物,支持以下子配置项,也支持覆盖外部的公共配置项。

input

  • 类型:string
  • 默认值:src

指定要转换的源码目录。

output

  • 类型:string
  • 默认值:<auto>

指定产物的输出目录,esm 产物的默认输出目录为 dist/esmcjs 产物的默认输出目录为 dist/cjs

transformer

  • 类型:babel | esbuild | swc
  • 默认值:<auto>

指定源码的编译工具,当 platformnode 时,默认值为 esbuild,当 platformbrowser 时,默认值为 babel

overrides

  • 类型:object
  • 默认值:undefined

为指定源码子目录覆盖构建配置,例如:

ts
export default {
  esm: {
    overrides: {
      // 将 server 文件夹下的源码以 node 为目标平台进行编译
      'src/server': {
        platform: 'node',
      },
    },
  },
}

ignores

  • 类型:string[]
  • 默认值:undefined

配置转换过程中需要忽略的文件,支持 glob 表达式,被匹配的文件将不会输出到产物目录。另外,father 会默认忽略源码目录中所有的 Markdown 文件和测试文件。

parallel

  • 类型:boolean
  • 默认值:false

指定是否开启并发编译,默认关闭。

umd

  • 类型:object
  • 默认值:undefined

配置将源码打包为 UMD 产物,支持以下子配置项,也支持覆盖外部的公共配置项。

name

  • 类型:string
  • 默认值:无

指定 umd 包的导出 library 名称,例如:

ts
export default {
  umd: {
    name: 'fatherDemo',
  },
}

默认是全量导出 member exports,需要拆解 default 的话,可以通过 chainWebpack 配置修改 libraryExport,例如:

ts
export default {
  umd: {
    name: 'fatherDemo',
    chainWebpack: (memo) => {
      memo.output.libraryExport('default')
      return memo
    },
  },
}

extractCSS

  • 类型:boolean
  • 默认值:true

指定是否提取 CSS 为单独的文件,可通过设置 extractCSS: false 关闭。

entry

  • 类型:string | Record<string, Config>
  • 默认值:src/index

指定要打包的源码入口文件,支持配置多入口、并为每个入口文件单独覆盖构建配置,例如:

ts
export default {
  umd: {
    entry: {
      'src/browser': {},
      'src/server': {
        platform: 'node',
      },
    },
  },
}

output

  • 类型:string | { path?: string; filename?: string }
  • 默认值:dist/umd

指定产物的输出目录及输出文件名,输出目录的默认值为 dist/umd,输出文件名在单 entry 时默认以 NPM 包名命名、多 entry 时默认与源码文件同名。

externals

  • 类型:Record<string, string>
  • 默认值:undefined

配置源码打包过程中需要处理的外部依赖。

chainWebpack

  • 类型:function
  • 默认值:undefined

使用 webpack-chain 自定义源码打包的 Webpack 配置。

postcssOptions

  • 类型:object
  • 默认值:undefined

配置源码打包过程中额外的 PostCSS 配置项

autoprefixer

配置源码打包过程中额外的 Autoprefixer 配置项

theme

配置 Less 源码打包过程中要注入的 Less 变量。

ts
export default {
  theme: { 'primary-color': '#1890ff' },
}

prebundle

配置项目需要预打包的三方依赖,仅用于 Node.js 工具或框架项目降低安装体积、提升项目稳定性,例如 Umi 这类前端开发框架。

预打包支持以下配置项。

output

  • 类型:string
  • 默认值:compiled

指定预打包产物的输出目录,默认输出到compiled目录。

deps

  • 类型:string[] | Record<string, { minify?: boolean; dts?: boolean }>
  • 默认值:undefined

配置需要预打包的三方依赖,默认开启代码压缩、打包类型声明文件(如果是 TypeScript 项目且包含类型声明),且将每个依赖的打包产物输出到 [output]/[package_name] 目录下。

也可以单独对每个依赖进行配置,例如:

ts
export default {
  prebundle: {
    // 只配置要预打包的依赖
    deps: ['rimraf'],

    // 配置预打包的依赖并指定详细配置
    deps: {
      rimraf: { minify: false },
    },
  },
}

extraDtsDeps

  • 类型:string[]
  • 默认值:undefined

配置仅需要打包 d.ts 类型声明文件的依赖。

extraExternals

  • 类型:Record<string, string>
  • 默认值:undefined

配置预打包过程中要额外处理的外部依赖。father 会默认对以下两类依赖做 external:

  1. 预打包的所有目标依赖,并自动 external 到输出目录
  2. 当前项目 package.json 中声明的 dependencies

其他配置

plugins

  • 类型:string[]
  • 默认值:undefined

配置额外的 father 插件,可以是插件的路径或者 NPM 包名,如果是相对路径则会从项目根目录开始找。

插件编写方式与 Umi 插件类似,可以在插件函数体中接收 api 参数来控制 father 的行为,例如写一个插件修改默认配置:

ts
// plugin.ts
import type { IApi } from 'father'

export default (api: IApi) => {
  api.modifyConfig((memo) => {
    // 修改 father 配置
    return memo
  })
}

// .fatherrc.ts
import { defineConfig } from 'father'

export default defineConfig({
  plugins: ['./plugin.ts'],
})

presets

  • 类型:string[]
  • 默认值:undefined

配置额外的 father 插件集,可以是插件集的路径或者 NPM 包名,如果是相对路径则会从项目根目录开始找。

插件集的编写方式与 Umi 插件集类似,可以在插件集函数中返回插件配置,例如:

ts
// preset.ts
import type { IApi } from 'father'

export default (api: IApi) => {
  return {
    presets: [require.resolve('./other-preset')],
    plugins: [require.resolve('./plugin-a'), require.resolve('./plugin-b')],
  }
}

小结

本文介绍了father的配置文件,以及配置文件中的各个配置项的含义和用法。以及构建产物的区别喝使用场景,以后发布公共的代码为npm包时候不妨尝试一下这个工具。

如有转载或 CV 的请标注本站原文地址