complugin
Version:
Unified plugin system for bundle tools.(e.g. rollback, vite, webpack, esbuild)
420 lines (291 loc) • 10.9 kB
Markdown
[npm]: https://img.shields.io/npm/v/complugin
[npm-url]: https://www.npmjs.com/package/complugin
[size]: https://packagephobia.now.sh/badge?p=complugin
[size-url]: https://packagephobia.now.sh/result?p=complugin
# complugin
[![npm][npm]][npm-url]
[![size][size]][size-url]
适用于各种捆绑工具的统一插件系统。(e.g. [rollback](https://rollupjs.org/), [vite](https://vitejs.dev/), [webpack](https://webpack.js.org/), [esbuild](https://esbuild.github.io/)).
<p><a href="README.md">English</a> | 中文</p>
目前支持:
- [Rollup](https://rollupjs.org/)
- [Vite](https://vitejs.dev/)
- [Webpack](https://webpack.js.org/)
- [esbuild](https://esbuild.github.io/)
## 注意
1. 在`Webpack`中,请不要使用`thread-loader`。
2. 在`esbuild`中, 请使用`complugin.proxyEsbuild(esbuild).build({/* options */})`代替`esbuild.build({/* options */})`。
## 使用
```ts
import { createComplugin } from 'complugin'
interface Options {}
export default createComplugin<Options>({
name: 'my-first-complugin',
// 插件还可以指定强制属性来调整其应用程序顺序。enforce的值可以是"pre"或"post"。
enforce: 'pre',
factory(options, meta) {
// 根据不同的捆绑器做具体的事情。
switch (meta.framework) {
case 'rollup':
// ...
break
case 'vite':
// ...
break
case 'esbuild':
// ...
break
case 'webpack':
// ...
break
default:
// Other bundlers
}
return {
buildStart() {
// ...
},
resolveId(source, importer) {
// ...
},
load(id) {
// ...
},
transformInclude(id) {
// ...
return false
},
transform(code, id) {
// ...
},
buildEnd() {
// ...
},
generateBundle(bundle, rawBundle) {
// ...
}
}
}
})
```
### 插件安装
##### Vite
```ts
// vite.config.ts
import MyComplugin from './my-complugin'
export default {
plugins: [
MyComplugin.vite({
/* options */
}),
// Or
MyComplugin({
/* options */
}).vite
]
}
```
##### Rollup
```ts
// rollup.config.js
import MyComplugin from './my-complugin'
export default {
plugins: [
MyComplugin.rollup({
/* options */
}),
// Or
MyComplugin({
/* options */
}).rollup
]
}
```
##### Webpack
```ts
// webpack.config.js
const MyComplugin = require('./my-complugin').default
module.exports = {
plugins: [
MyComplugin.webpack({
/* options */
}),
// Or
MyComplugin({
/* options */
}).webpack
]
}
```
##### esbuild
```ts
// esbuild.config.js
import _esbuild from 'esbuild'
import { proxyEsbuild } from 'complugin'
import MyComplugin from './my-complugin'
// Cannot be omitted
const esbuild = proxyEsbuild(_esbuild)
esbuild.build({
plugins: [
MyComplugin.esbuild({
/* options */
}),
// Or
MyComplugin({
/* options */
}).esbuild
]
})
```
## Hooks
> `complugim`将优秀的`Rollup插件API`作为参考,并为各种捆绑器提供统一的`hook-API`。
### `buildStart`
#### Type: `(this: CompluginMinifyContext) => void`
#### Kind: `async`
#### Next Hook: `resolveId`
当捆绑器开始构建时调用。
### `resolveId`
#### Type: `(source: string, importer?: string) => string | { id: string, external?: boolean } | { name?: string, source: string | Buffer } | null`
#### Kind: `async`
#### Next Hook: `load` (如果尚未加载已解析的 id)
定义自定义解析器。解析器可用于定位第三方依赖关系。
返回`null`将遵循其他`resolveId`函数,并最终遵循默认的解析行为。
如果返回了一个对象并设置了字符串属性`"id"`,则可以将导入解析为不同的 id,并从包中排除。这允许您将依赖项替换为外部依赖项,而无需用户通过外部选项手动将其标记为`"外部"`。
如果返回了一个对象并设置了属性`"source"`,则将`"source"`的值作为资产内容,并发出一个资产文件。
### `load`
#### Type: `(this: CompluginContext, id: string) => string | { code: string, map?: object } | null`
#### Kind: `async`
#### Next Hook: `transformInclude` (如果没有使用缓存,或者没有具有相同代码的缓存副本)
定义自定义加载程序。返回`null`将遵循其他`load`函数(最终是从文件系统加载的默认行为)。这个钩子可以选择性地返回`{code,map}`对象。
### `transformInclude`
#### Type: `(id: string) => boolean`
#### Next Hook: `transform` (如果返回`true`)
是否启用转换。如果未设置此钩子,则启用所有转换。
### `transform`
#### Type: `(this: CompluginContext, code: string, id: string) => string | { code: string, map?: object } | null`
可用于转换单个模块。这个钩子可以选择性地返回`{code,map}`对象。如果转换不移动代码,则可以通过将映射设置为`null`来保留现有的`sourcemaps`。否则,您可能需要生成源映射。
### `buildEnd`
#### Type: `(this: CompluginMinifyContext) => void`
#### Kind: `async`
#### Next Hook: `generateBundle`
绑定工具完成绑定时调用。
### `generateBundle`
#### Type: `(bundle: { [fileName: string]: OutputFile }, rawBundle: unknown) => void`
当绑定工具生成 bundle 对象时调用。
通过从这个钩子中的 bundle 对象中删除文件,可以防止发出文件。
## Hooks Supported
| Hook | Rollup | Vite | Webpack4 | Webpack5 | esbuild |
| ----------------------------------------------------------------------------- | :----: | :--: | :------: | :------: | :-----: |
| [`buildStart`](https://rollupjs.org/guide/en/#buildstart) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [`resolveId`](https://rollupjs.org/guide/en/#resolveid) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [`load`](https://rollupjs.org/guide/en/#load) | ✅ | ✅ | ✅ | ✅ | ✅ |
| `transformInclude`<sup>1</sup> | ✅ | ✅ | ✅ | ✅ | ✅ |
| [`transform`](https://rollupjs.org/guide/en/#transformers)<sup>2</sup> | ✅ | ✅ | ✅ | ✅ | ✅ |
| [`buildEnd`](https://rollupjs.org/guide/en/#buildend) | ✅ | ✅ | ✅ | ✅ | ✅ |
| [`generateBundle`](https://rollupjs.org/guide/en/#generatebundle)<sup>3</sup> | ✅ | ✅ | ✅ | ✅ | ✅ |
1. `Webpack`的 id 过滤器在加载程序逻辑之外;为了在`webpack`上获得更好的性能,需要额外的钩子。在`rollup`和`vite`中,这个钩子被多填充以匹配行为。
2. 尽管`esbuild`可以处理 JavaScript 和 CSS 以及许多其他文件格式,但只能在“load”和“transform”结果中返回 JavaScript。
3. 为了与各种捆绑工具兼容,该挂钩在设计时与`rollup`的`generateBundle`挂钩不兼容。
## `CompluginMinifyContext`
### `emitFile`
#### Type: `(asset: { name?: string, fileName?: string, source: string | Buffer }) => void`
发出包含在生成输出中的新资源文件。
### `error`
#### Type: `(message: string) => never | void`
在结构上等同于`this.warn`.
警告,但当绑定器为“rollup”或“vite”时,它也会中止绑定过程。
### `warn`
#### Type: `(message: string) => void`
使用此方法将对生成的警告进行排队。
### `resolve`
#### Type: `(source: string, importer?: string) => Promise<{ id: string; external?: boolean } | undefined>`
使用捆绑器使用的相同插件将导入解析为模块 ID(即文件名),并确定导入是否应该是外部的。如果返回 null,则绑定器或任何插件都无法解析导入,但用户未明确将其标记为外部。
在`webpack`中,某些模块 id 无法正确识别为外部。
## `CompluginContext`
### `emitAsset`
#### Type: `(asset: { name?: string, fileName?: string, source: string | Buffer }): string`
发出一个包含在生成输出中的新资源文件,并返回一个占位符表达式,该表达式可在不同位置用于引用发出的文件。
例如:
```ts
const hooks = {
// ...others hook,
load(id) {
const placeholder = this.emitAsset({ name: 'hello world.txt', source: 'hello world!' })
return `
import { readFile } from 'fs/promises'
const fileName = ${placeholder}
readFile(fileName).then((content) => {
console.log(content.toString()) // hello world!
})
`
}
}
```
### `addWatchFile`
#### Type: `(fileName: string) => void`
添加要在监视模式下监视的其他文件,以便对这些文件的更改将触发重建。
## API
### `createComplugin`
#### Type: `<UserOptions = {}>(args: CreateCompluginArgs<UserOptions>) => CompluginInstance<UserOptions>`
create a complugin.
### `utils`
plugin utils.
### `proxyEsbuild`
#### Type: `(esbuild: typeof import('esbuild')) => typeof import('esbuild')`
At present, the generated esbuild-plugin needs to run in the esbuild environment proxied by `proxyEsbuild()`.
### `registerCompluginGenerator`
#### Type: `(framework: string, generator: <UserOptions = {}>(args: CreateCompluginArgs<UserOptions>, userOptions?: UserOptions) => any) => void`
Register custom plugin generator.
e.g
```ts
import { registerCompluginGenerator, commonInputFS } from 'complugin'
import MyComplugin from './my-complugin'
// Register
registerCompluginGenerator('custom', function ({ name, enforce, factory }, options) {
const meta = {
// required
framework: 'custom',
version: '1.0.0',
inputFS: { ...commonInputFS }
// ...others
}
const {
buildStart,
resolveId,
load,
transformInclude = () => true,
transform,
buildEnd,
generateBundle
} = factory(options, meta)
return // ...
})
// Usage
const createdCustomPlugin = MyComplugin.custom({
/* options */
})
const createdCustomPluginOr = MyComplugin({
/* options */
}).custom
```
## Give a ⭐️ if this project helped you!
## License
[MIT](./LICENSE) License © 2022 xxXyh1908