Skip to content
forked from ice-lab/icepkg

The Next Generation of Package Bundler

License

Notifications You must be signed in to change notification settings

dongshihao/icepkg

 
 

Repository files navigation

@ice/pkg

NPM 包的开发解决方案。

特性

  • 支持导出 esm, cjs, esnext 三种格式的产物
  • 支持 bundle 和 transform 两种模式
  • 支持使用 docusaurus 作为文档生成方案
  • 支持 React 组件预览
  • 基于 Rollup & SWC

安装

在项目下安装:

$ npm i -D @ice/pkg

# 或通过 pnpm 安装
$ pnpm i -D @ice/pkg

使用

# 构建
$ ice-pkg build

# 实时编译 es&lib 产物
$ ice-pkg start

# 实时编译 dist 产物
$ ice-pkg start --dist

# 组件预览
$ ice-pkg start --doc

编写代码限制

默认情况下,src 文件夹存放所书写的源代码。在源代码的写法中,有以下限制:

  1. 添加文件后缀

比如,以下 TypeScript 代码:

import { add } from './filename'; // 引用相对路径的文件
import { mulitply } from './directory'; // 引用相对路径下的默认的 index.js 文件

...

在 esm 规范下,以上的写法属于不规范写法,因此 @ice/pkg 亦不支持上述写法。正确的写法如下:

import { add } from './filename.js'; // 引用相对路径的文件
import { mulitply } from './directory/index.js'; // 引用相对路径下的默认的 index.js 文件

...

若主动开启 autoPathcompletion 则在构建产物中会自动进行路径补全,该配置默认为 false

  1. 不支持 require、__dirname用法

不支持的方法或变量主要有:

  • requirerequire.resolverequire.cache
  • __dirname__filename

Package Exports

目前,Package 导出依赖 mainmoduleexports 等导出配置。@ice/pkg 默认的导出配置为:

{
  ...
  "module": "es/index.js",
  "exports": {
    "esnext": "./esnext/index.js",
    "import": "./es/index.js",
    "default": "./es/index.js"
  }
}

其中 module 配置可供给不支持 exports 的浏览器端构建工具使用(比如 webpack4)。

如何消费 esnext 产物

Node 和其他浏览器端工具并不默认消费 esnext 产物。以 webpack5,我们介绍如何在浏览器端使用 esnext 产物。

  1. 通过 conditionNames 使得 webpack 能够 resolve Package 的 esnext 产物。
module.exports = {
  //...
  resolve: {
    conditionNames: ['esnext'],
  },
};
  1. 一般来说,esnext 产物需要根据应用的目标浏览器进行二次编译。比如:
module.exports = {
  module: {
    rules: [
      test: '/\.js$/',
      use: 'swc-loader',
      include: [
        path.resolve(precess.cwd(), 'src'),
        path.resolve(precess.cwd(), 'node_modules/packageName'),
      ]
    ]
  }
};

编译 CommonJs 的 Packge Exports

若开启 lib 配置,则 @ice/pkg 会编译出 CommonJs 产物。此时,推荐的 Package Exports 配置如下:

{
  ...
+ "main": "lib/index.js",
  "module": "es/index.js",
  "exports": {
    "esnext": "./esnext/index.js",
    "import": "./es/index.js",
+   "require": "./lib/index.js",
-   "default": "./es/index.js"
+   "default": "./lib/index.js"
  }
}

更多有关 Package Exports 的知识可阅读 Webpack Package ExportsNode Package

配置

@ice/pkg 支持的配置文件有:

  • build.json
  • build.config.js
  • build.config.ts
  • build.config.mjs
  • build.config.mts

推荐使用 build.config.ts 进行配置,并引入 defineConfig 以获得更好的类型提示:

// build.config.ts
import { defineConfig } from '@ice/pkg';

export default defineConfig({
  minify: true,
})

在没有配置 type="module" 的 Package 中, 配置文件 build.config.[j|t]s 仍推荐使用 esm 规范语法,而非 CommonJs 规范语法。

所有配置项如下:

alias

  • 类型 object
  • 默认值 {}

比如,将 @ 指向 ./src/ 目录。

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  alias: {
    '@': './src/'
  }
})

minify

  • 类型 boolean
  • 默认 false

设置为 false 可以禁用代码混淆能力。@ice/pkg 使用 swc 进行混淆。

define

  • 类型 object
  • 默认 {}

配置全局变量,在构建时会被静态替换。

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  define: {
    __APP_VERSION__: '1.1.0'
  }
})

babalPlugins

  • 类型 array
  • 默认 []

可以使用 babelPlugin 来预处理一些代码片段。被 babelPlugin 处理过的代码,接下来仍会交给 swc 做进一步的处理。

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  plugins: [["transform-remove-console", { "exclude": ["error", "warn"] }]]
})

generateTypesForJs

  • 类型 boolean
  • 默认 false

为 JavaScript 代码生成类型文件。@ice/pkg 默认为文件后缀为 .ts 生成类型文件。

如果使用 jsdoc 为 JavaScript 生成了类型注解,该配置会非常有效。

// 下面通过 jsdoc 进行了类型注解
/**
 *
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */
export function add(a, b) {
  return a + b;
}

若开启该配置,则生成如下 .d.ts 文件:

export function add(a: number, b: number): number;

lib

  • 类型 boolean
  • 默认 false

@ice/pkg 默认生成 esm 模块规范的代码。esm 模块已广泛应用于 Node(^12.20.0 || ^14.13.1 || >=16.0.0)和 Browser 端(webpack 4 & webpack 5 & vite & rollup)。

若需要生成 commonjs 规范的产物,可以配置该选项,则会生成 lib 文件目录,存放 commonjs 产物。

相应地,可能需要修改 Package Exports 导出的配置。

autoPathcompletion

  • 类型 boolean
  • 默认 false

是否为不规范的 esm 导入提供自动路径补全。

plugins

  • 类型 array
  • 默认 []

@ice/pkg 基于 build-scripts 插件系统。更多内容请参考 插件开发

umd

  • 类型 object
  • 默认 {}

@ice/pkg 提供过渡性的 umd bundle 产物。配置开启,则生成 dist 文件目录,存放 bundle 产物。

umd 配置有以下参数:

name
  • 类型 string
  • 默认 package.name

library 导出的名称,可以通过 window[name] 访问。默认为 package.json 配置的 name 字段。

filename
  • 类型 string
  • 默认 index.js

生成的文件名,默认为 index.js

sourceMaps
  • 类型 boolean | 'inline'
  • 默认 false

是否生成 sourcemap,这在代码调试的时候非常有用。

minify
  • 类型 boolean | object
  • 默认 true

混淆代码。也可以配置具体的 混淆策略@ice/pkg 使用 swc 进行代码混淆。

env
  • 类型 string | object
  • 默认 { mode: "usage", coreJs: "3", targets: { chrome: 49, ie: 11 } }

一次性配置 polyfills 和 browserlist。

若配置为 es3 | es5 | es2015 | es2016 | es2017 | es2018 | es2019 | es2020 | es2021 | es2022,则只会进行对应的语法转换。

若配置为:

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  umd: {
    env: {
      mode: 'usage',
      coreJs: '3',
      targets: {
        chrome: 80
      }
    }
  }
})

@ice/pkg 会根据对应的 browserlist 进行对应的语法转换,以及添加对应的 polyfills。

插件开发

@ice/pkg 基于 build-scripts 插件系统。通过 build-scripts 插件,可以极大地扩展 @ice/pkg 的能力。

插件的使用如下:

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  plugins: [
    "./customPlugin.ts"
  ]
})

修改默认配置

可以通过 onGetConfig API,可以修改 Package 编译的入口、出口等 @ice/pkg 等默认配置:

const plugin = (api) => {
  const { context, onGetConfig } = api;
  const { rootDir } = context;

  onGetConfig('component-es', config => {
    return (
      ...config,
      outputDir: path.join(rootDir, 'esm'), // 将出口修改为 esm 文件夹
    )
  })
}

@ice/pkg 注册三个 build-script 任务:

  • component-es - 主任务,默认启动
  • component-esnext - 主任务,默认启动
  • component-lib - 当开启 lib 配置时启动
  • component-dist - 当开启 dist 配置时启动

当不指定任务名(比如,指定 component-es)时,配置对所有任务生效。

import svelte from 'rollup-plugin-svelte';

const plugin = (api) => {
  const { context, onGetConfig } = api;
  const { rootDir } = context;

  // 不指定 Task name
  onGetConfig(config => {
    return (
      ...config,
      rollupPlugins: [
        svelte(...) // 编译 svelte 文件则会进行对应的
      ]
    )
  })
}

有以下参数可以配置:

entry
  • 类型 string
  • 默认值 ./src | ./src/index.[j|t]s

配置组件编译的入口。

任务 默认值
component-es ./src
component-esnext ./src
component-lib ./src
component-dst `./src/index[j
outputDir
  • 类型 string
  • 默认值 es | lib | dist

配置组件编译的出口。

任务 默认值
component-es es
component-esnext esnext
component-lib lib
component-dst dist
rollupPlugins
  • 类型 array
  • 默认值 []

配置额外的 rollupPlugins

rollupOptions
  • 类型:object
  • 默认值 {}

当开启 lib 选项,可通过 rollupOptions 配置额外的 rollup 配置

当试图修改 rollupOptions.plugins 参数时,建议直接使用 rollupPlugins 参数。

swcCompileOptions
  • 类型 array
  • 默认值 {}

swc 编译选项。具体可参考 swc 配置

插件生命周期钩子

@ice/pkg 插件提供一下生命周期钩子:

  • build 命令:
生命周期 参数 调用时机
before.build.load { args: CommandArgs; config: ComponentConfig[] } 获取所有任务配置后
before.build.run { args: CommandArgs; config: ComponentConfig[] } 编译执行之前
after.build.compile - 编译结束
error { errCode: string; err: Error } 错误
  • start 命令
生命周期 参数 调用时机
before.start.load { args: CommandArgs; config: ComponentConfig[] } 获取所有任务配置后
before.start.run { args: CommandArgs; config: ComponentConfig[] } 编译执行之前
after.start.compile - 编译结束
error { errCode: string; err: Error } 错误

其他有关插件的使用可参考 build-scripts 插件开发

组件预览

@ice/pkg 依赖 @ice/pkg-plugin-docusaurus 插件支持编写文档和预览组件,所有文档默认存放至 docs 文件夹下。支持以 .md.mdx 为后缀的文档。用法:

# 若存在 docs 文件夹,则默认启动文档预览;并启动 es/lib 编译
$ ice-pkg start

# 不启动文档预览
$ ice-pkg start --doc=false

# 若存在 docs 文件夹,则默认构建预览产物
$ ice-pkg build

如何书写文档

文档以 .md.mdx 为后缀,采用 yamlmarkdown 语法。

---
title: Simple Usage
sidebar_position: 1
---

## 本 Demo 演示一行文字的用法

+ 将代码块渲染成源代码

```jsx
import MyComponent from 'my-component';
import './my-component.css';

const App = () => {
  return (
    <div>
      <MyComponent />
    </div>
  )
}

export default MyComponent;
```

+ 将代码渲染成组件

```jsx preview
import MyComponent from 'my-component';
import './my-component.css';

const App = () => {
  return (
    <div>
      <MyComponent />
    </div>
  )
}

export default MyComponent;

扁平结构

扁平结构的含义是可以将文档平铺在 docs 文件夹下:

.
├── index.md
└── intro.md

嵌套结构

此外还支持嵌套结构,比如:

.
├── Foo
│   ├── Basic.md
│   └── Complex.md
├── index.md
└── intro.md

文档排序

文档默认按照文件(或文件夹)的字母顺序进行排列。若要修改排列顺序,可通过下面两种方式。

  • 使用 sidebar_position

可以在文档头部使用 YAML 标记顺序,比如想要将 index.md 设置为:

---
sidebar_position: 0
---

# 这是首页,同时也是标题

在此处描述首页说明信息
  • 文档添加数字前缀
.
├── 01-intro.md
├── 02-Foo
│   ├── 01-Basic.md
│   └── 02-Complex.md
└── index.md

文档标题

文档会默认使用第一个 markdown 标题,作为文档的 title。此外,可以通过 yaml 语法来修改文档标题:

---
sidebar_label: 这是标题
---

# 这里不再是默认标题了

在此处描述文档内容...

代码块

将代码渲染成组件

若想要预览组件,需要给代码块添加 preview 属性。

import MyComponent from 'my-component';
import style from './style.module.css';

const App = () => {
  return (
    <div className={styles.container}>
      <MyComponent />
    </div>
  )
}

export default MyComponent;

则会渲染成下面的样子:

需要注意的是,在 preview 的代码块中引入的样式会 污染 全局,因此建议使用 css-modulecss-in-js 等方式引入样式。

给代码块添加 title

若想要给代码块添加 title,可以使用 title 属性。

import MyComponent from 'my-component';
...

插件配置

@ice/pkg-plugin-docusaurus 插件接受如下配置:

export interface PluginDocusaurusOptions {
  /**
   * 文档的 title,默认为 "飞冰组件"
   */
  title?: string;
  /**
   * 文档部署的顶层 url。比如部署在 github,则是 https://你的项目.github.io
   */
  url?: string;
  /**
   * 文档路由的 baseUrl。
   */
  baseUrl?: string;
  /**
   * 文档站点的 favicon 文件位置,默认为 static/img/favicon.ico
   */
  favicon?: string;
  /**
   * 侧边栏 logo,默认为 static/img/logo.png
   */
  navBarLogo?: string;
  /**
   * 侧边栏 title,默认为 "飞冰组件"
   */
  navBarTitle?: string;
  /**
   * 文档启动的端口,默认为 4444
   */
  port?: number;
};

使用方式如下:

import { defineConfig } from '@ice/pkg';

export default defineConfig({
  plugins: [
    ['@ice/pkg-plugin-docusaurus', {
      title: '标题'
    }]
  ]
})

自定义 Docusaurus 能力

若想要完整的 Docusaurus 能力,可在工程下自定义 docusaurus.config.js。具体的使用方式参考 Docusaurus 文档

Contributing

Please see our CONTRIBUTING.md

License

MIT

About

The Next Generation of Package Bundler

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 88.9%
  • JavaScript 9.1%
  • Handlebars 1.2%
  • CSS 0.8%