---
title: "编写 JavaScript 插件"
locale: "zh"
version: "1.x"
canonicalUrl: "https://farmfe.org/zh/docs/1.x/plugins/writing-plugins/js-plugin"
publicationStatus: "prerelease"
---

<a id="编写-javascript-插件"></a>
# 编写 JavaScript 插件


一个 JavaScript 插件只是一个定义了一系列 hooks 的纯 JavaScript 对象:


```js
// farm.config.ts
import { defineConfig } from "@farmfe/core";

export default defineConfig({
  // ...
  plugins: [
    // 一个插件对象
    {
      name: "my-resolve-plugin",
      priority: 1000, // 插件的优先级越高越早执行 通常的优先级是 100
      resolve: {
        filters: {
          // 仅仅符合下面的条件才会执行 hook
          sources: ["\\./index.ts"], // 正则表达式的数组
          importers: ["None"],
        },
        executor: async (param) => {
          // hook 执行器
          console.log(param); // 解析 param
          // 返回最终的结果
          return {
            resolvedPath: "virtual:my-module",
            query: {},
            sideEffects: false,
            external: false,
          };
        },
      },
    },
    // Load、transform和 resolve 类似, 引用他们的类型
  ],
});
```


如果你想向插件传递参数，可以使用闭包


```ts
// my-resolve-plugin.ts
export function myResolvePlugin(options: Options) {
  const { xx } = options;

  return {
    name: "my-resolve-plugin",
    resolve: {
      // ...
    },
  };
}

// farm.config.ts
import { defineConfig } from "@farmfe/core";
import { myResolvePlugin } from "./myResolvePlugin.ts";

export default defineConfig({
  // ...
  plugins: [myResolvePlugin({ xx: "xx" })],
});
```



> [!NOTE]
> 备注
> 
> - 参考 [创建插件](</zh/docs/1.x/plugins/writing-plugins/js-plugin#create-plugin>)，可以根据官方模版快速写一个新的插件
> - 本文档仅介绍如何创建、开发和发布一个 JavaScript 插件，有关插件 hook 的更多细节，请参阅[Javascript 插件 Hooks](</zh/docs/api/js-plugin-api>)



<a id="约定"></a>
## 约定


对于特定的 Farm JavaScript 插件


- 一个 Farm 的 JavaScript 插件应该有一个 ` farm-plugin- ` 前缀的名称并且语义清晰
- package.json 里面有 ` farm-plugin- ` 关键字


如果你的 JavaScript 插件仅仅适配特定框架，其名称应遵循以下前缀格式:


- ` farm-plugin-vue- `: 作为 Vue 插件前缀
- ` farm-plugin-react- `: 作为 React 插件前缀
- ` farm-plugin-svelte- `: 作为 Svelte 插件前缀
- ...


<a id="概念"></a>
## 概念


在开始编写 JavaScript 插件之前，你应该了解以下概念:


- **filters**： 由于 JavaScript 插件运行要比 Rust 插件慢得多，你的 JavaScript 插件应该显式的设置 filter 来避免不必要的 hook 调用。 举个例子，你应该设置 ` transform.filters.moduleTypes = ['js'] ` 来确保你的 JavaScript 插件 transform 钩子仅仅在 ` .js/mjs/cjs ` 文件执行
- **module\_type**：模块的类型，他可能是 ` js `, ` ts `, ` css `, ` sass `, ` json ` 等等。Farm 原生支持 ` js/ts/jsx/tsx `, ` css `, ` html `, ` json `, ` static asserts(png, svg等等) `。` module_type ` 会被 ` load ` 或者 ` transform ` 钩子返回
- **resolved\_path 和 module\_id**：` resolved_path ` 是一个模块的绝对路径，` module_id `是一个模块的唯一 id，通常是` 模块对于项目根目录的相对路径 ` + ` query `。例如 我们引用了一个模块 ` import './a?query' ` resolved\_path 是 ` /project/src/a.ts ` module\_id 是 ` src/a.ts?query `
- **context**: 所有的插件都会接受一个 ` context ` 参数，它有 Farm 项目的整个编译上下文，你可以从里面拿到 ModuleGraph，Module，Resources等等
- **Resource and Resource Pot**：` Resource ` 是输出出来的最终打包产物，`  Resource Pot `是资源的抽象表示，类似于其他打包器的 ` Chunk `。在 Farm 项目中，我们首先从 ` ModuleGraph ` 生成 ` Resource Pots `, 渲染 ` Resource Pots `，最终从 ` Resource Pots ` 生成 ` Resource `


<a id="filters"></a>
### Filters


由于 ` JavaScript插件 ` 比 ` Rust插件 ` 慢得多，Farm 使用 ` filters ` 来控制 JavaScript 插件钩子的执行。为了提高性能，只有匹配当前的 ` filters ` ，插件钩子才会执行。` filters ` 对于一些常用的钩子是必需的，例如` resolve `， ` load `， ` transform `等。
例如，如果你想转换 css 文件，你可以使用` transform.filters. moduletypes = ['css'] `来确保 JavaScript 插件的 transform 钩子只对` .css `文件运行:


```ts
const myCssPlugin = {
  name: "my-css-plugin",
  transform: {
    filters: {
      // Only execute the hook when following conditions satisfied
      // resolvedPaths: ["\\./index.ts"], // a regex array to match the resolvedPaths
      moduleTypes: ["css"],
    },
    executor: async (param) => {
      // transform css
    },
  },
};
```


<a id="module-type"></a>
### Module Type


在 Farm 中，一切都被认为是“一等公民”，因此 Farm 设计 ` module_type ` 来标识模块类型，并在用不同的插件处理不同的模块类型
` Module_type  `由 ` load ` 钩子返回，并且可以由 ` transform ` 钩子转换。Farm 原生支持 ` js/ts/jsx/tsx `、` css `、` html `、` json `、` static assets(png、svg等) `。对于这些模块类型，你可以直接在 ` load ` 或 ` transform ` hook 中返回。但是如果你想处理自定义模块类型，你需要实现其他钩子例如 ` parse `， ` render_resource_pot_modules `， ` generate resources ` 等来控制如何对自定义模块进行类型解析，渲染和生成资源。


<a id="create-plugin"></a>
## 创建插件


Farm 提供了官方模板来帮助你快速创建 JavaScript 插件:


**pnpm**

```javascript
pnpm create farm-plugin
```


**npm**

```javascript
npm create farm-plugin@latest
```


**yarn**

```javascript
yarn create farm-plugin
```



然后按照提示创建插件


或者直接运行以下命令创建插件:


**pnpm**

```javascript
pnpm create farm-plugin my-farm-plugin --type js
```


**npm**

```javascript
npm create my-farm-plugin --type js
```


**yarn**

```javascript
yarn create my-farm-plugin --type js
```



上面的命令会在当前目录中创建一个名为 ` my-farm-plugin ` 的js插件。` ——type ` 可以是 ` rust ` 或者 ` js `


<a id="开发一个插件"></a>
## 开发一个插件


创建插件后，就可以开始开发插件了。这个插件只是一个定义了一系列 hooks 的纯 JavaScript 对象:


```ts
// import { readFileSync } from 'node:fs';
import type { JsPlugin } from '@farmfe/core';

interface Options {
  /* Your options here */
}

export default function farmPlugin(options: Options): JsPlugin {
  return {
    name: '<FARM-JS-PLUGIN-NPM-NAME>',
    /* Your plugin hooks here: */

    // transform: {
    //   filters: {
    //     moduleTypes: ['js']
    //   },
    //   async executor(params) {
    //     const { content } = params;
    //     return {
    //       content,
    //       moduleType: 'js'
    //     };
    //   }
    // },
    // finish: {
    //   executor() {}
    // }
  };
}
```



> [!TIP]
> 提示
> 
> 有关插件钩子的更多细节, 参阅[Javascript 插件 Hooks](</zh/docs/api/js-plugin-api>).



运行 ` npm Run dev ` 来编译插件开启监听。运行 ` npm Run build ` 来构建插件


<a id="发布插件"></a>
## 发布插件


JavaScript 插件是一个普通的 npm 包，你可以通过运行 ` npm publish ` 将其发布到 npm registry。
