UNPKG

complugin

Version:

Unified plugin system for bundle tools.(e.g. rollback, vite, webpack, esbuild)

439 lines (301 loc) 11.7 kB
[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] Unified plugin system for bundle-tools.(e.g. [rollback](https://rollupjs.org/), [vite](https://vitejs.dev/), [webpack](https://webpack.js.org/), [esbuild](https://esbuild.github.io/)). <p>English | <a href="README.zh.md">中文</a></p> Currently supports: - [Rollup](https://rollupjs.org/) - [Vite](https://vitejs.dev/) - [Webpack](https://webpack.js.org/) - [esbuild](https://esbuild.github.io/) ## Note 1. In `Webpack`, please don't use `thread-loader`. 2. In `Rollup`, `Complugin` cannot be used as an output plugin. 3. In `esbuild`, please use `complugin.proxyEsbuild(esbuild).build({/* options */})` instead of `esbuild.build({/* options */})`. --- ## Usage ```ts import { createComplugin } from 'complugin' interface Options {} export default createComplugin<Options>({ 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) { // ... } } } }) ``` ### Plugin Installation ##### 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](https://rollupjs.org/guide/en/#plugins-overview) 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`](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`'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 ` import { readFile } from 'fs/promises' const fileName = ${placeholder} readFile(fileName).then((content) => { console.log(content.toString()) // hello world! }) ` } } ``` --- #### `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: `<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