Unified plugin system for bundle tools.(e.g. rollback, vite, webpack, esbuild)
[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
English | 中文
Webpack, please don't use thread-loader.
Rollup, Complugin cannot be used as an output plugin.
esbuild, please use complugin.proxyEsbuild(esbuild).build({/ options /}) instead of esbuild.build({/ options /}).
ts
import { createComplugin } from 'complugin'
interface Options {}
export default createComplugin({
name: 'my-first-complugin',
// A complugin can additionally specify an enforce property to adjust its application order. The value of enforce can be either "pre" or "post".
enforce: 'pre',
factory(options, meta) {
// Do specific things according to different bundlers.
switch (meta.framework) {
case 'rollup':
// ...
break
case 'vite':
// ...
break
case 'esbuild':
// ...
break
case 'webpack':
// ...
break
default:
// Other bundlers
}
return {
buildStart() {
// ...
},
resolveId(importee, importer) {
// ...
},
load(id) {
// ...
},
transformInclude(id) {
// ...
return false
},
transform(code, id) {
// ...
},
buildEnd() {
// ...
},
generateBundle(bundle, rawBundle) {
// ...
}
}
}
})
`
$3
##### 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
> complugin takes the excellent Rollup plugin API as a reference, and provides a unified hooks-API for various bundle-tools.
#### buildStart
##### Type: (this: CompluginMinifyContext) => void
##### Kind: async
##### Next Hook: resolveId
Called when the bundle-tool starts building.
---
#### resolveId
##### Type: (importee: string, importer?: string) => string | { id: string, external?: boolean } | { name?: string, source: string | Buffer } | null
##### Kind: async
##### Next Hook: load if the resolved id that has not yet been loaded
Defines a custom resolver. A resolver can be useful for e.g. locating third-party dependencies.
Returning null defers to other resolveId functions and eventually the default resolution behavior.
If an object is returned and the string attribute "id" is set, the import can be resolved to a different ID and excluded from the bundle. This allows you to replace dependencies with external dependencies without requiring users to manually mark them as "external" through external options.
If an object is returned and the attribute "source" is set, the value of "source" is taken as the asset content and an asset file is emitted.
---
#### load
##### Type: (this: CompluginContext, id: string) => string | { code: string, map?: object, ast?: AcornNode } | { code: string, copy: true } | null
##### Kind: async
##### Next Hook: transformInclude if no cache was used, or there was no cached copy with the same code
Defines a custom loader. Returning null defers to other load functions (and eventually the default behavior of loading from the file system). this hook can optionally return a { code, map, ast } object.
---
#### transformInclude
##### Type: (id: string) => boolean
##### Next Hook: transform if true is returned
Whether transform is enabled. If this hook is not set, all transformations are enabled.
---
#### transform
##### Type: (this: CompluginContext, code: string, id: string, ast?: AcornNode) => string | { code: string, map?: object, ast?: AcornNode } | null
Can be used to transform individual modules. this hook can optionally return a { code, map, ast } object. If the transformation does not move code, you can preserve existing sourcemaps by setting map to null. Otherwise you might need to generate the source map.
---
#### buildEnd
##### Type: (this: CompluginMinifyContext) => void
##### Kind: async
##### Next Hook: generateBundle
Called when bundler-tool has finished bundling.
---
#### generateBundle
##### Type: (bundle: { [fileName: string]: OutputFile }, rawBundle: unknown) => void
Called when bundler-tool has generated bundle object.
You can prevent files from being emitted by deleting them from the bundle object in this hook.
---
Hooks Supported
| Hook | Rollup | Vite | Webpack4 | Webpack5 | esbuild |
| ----------------------------------------------------------------------------- | :----: | :--: | :------: | :------: | :-----: |
| buildStart | ✅ | ✅ | ✅ | ✅ | ✅ |
| resolveId | ✅ | ✅ | ✅ | ✅ | ✅ |
| load | ✅ | ✅ | ✅ | ✅ | ✅ |
| transformInclude1 | ✅ | ✅ | ✅ | ✅ | ✅ |
| transform2 | ✅ | ✅ | ✅ | ✅ | ✅ |
| buildEnd | ✅ | ✅ | ✅ | ✅ | ✅ |
| generateBundle3 | ✅ | ✅ | ✅ | ✅ | ✅ |
1. Webpack's id filter is outside of loader logic; an additional hook is needed for better perf on Webpack. In Rollup and Vite, this hook has been polyfilled to match the behaviors.
2. Although esbuild can handle both JavaScript and CSS and many other file formats, you can only return JavaScript in load and transform results.
3. In order to be compatible with various construction tools, this hook is not compatible with rollup's generateBundle-hook at design.
---
CompluginMinifyContext
#### emitFile
##### Type: (asset: { name?: string, fileName?: string, source: string | Buffer }) => void
Emits a new asset file that is included in the build output.
---
#### error
##### Type: (message: string) => never | void
Structurally equivalent to this.warn, except that it will also abort the bundling process when the bundler is rollup or vite.
---
#### warn
##### Type: (message: string) => void
Using this method will queue warnings for a build.
---
#### resolve
##### Type: (importee: string, importer?: string) => Promise<{ id: string; external?: boolean } | undefined>
Resolve imports to module ids (i.e. file names) using the same plugins that bundler uses, and determine if an import should be external. If null is returned, the import could not be resolved by bundler or any plugin but was not explicitly marked as external by the user.
In webpack, some module-id cannot be correctly identified as external.
---
#### parse
##### Type: (input: string, options?: AcornOptions) => AcornNode
Use acorn library to parse code to an AST.
---
CompluginContext
#### emitAsset
##### Type: (asset: { name?: string, fileName?: string, source: string | Buffer }): string
Emits a new asset file that is included in the build output and returns a placeholder-expression that can be used in various places to reference the emitted file.
e.g:
`ts
const hooks = {
// ...others hook,
load(id) {
const placeholder = this.emitAsset({ name: 'hello world.txt', source: 'hello world!' })
return
}
}
`
---
#### addWatchFile
##### Type: (fileName: string) => void
Adds additional files to be monitored in watch mode so that changes to these files will trigger rebuilds.
---
#### rebuild(New)
##### Type: () => void
force rebuild.
---
API
#### createComplugin
##### Type:
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:
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
``