Writing JavaScript Plugins
A JavaScript plugin is simply a pure JavaScript object that defines a set of property hooks:
// farm.config.ts
import { defineConfig } from "@farmfe/core";
export default defineConfig({
// ...
plugins: [
// a plugin object
{
name: "my-resolve-plugin",
priority: 1000, // the priority of this plugin, the larger the value, the earlier the execution. Normally internal plugins is 100.
resolve: {
filters: {
// Only execute the hook when following conditions satisfied
sources: ["\\./index.ts"], // a regex array
importers: ["None"],
},
executor: async (param) => {
// this hook executor
console.log(param); // resolve params
// return the resolve result
return {
resolvedPath: "virtual:my-module",
query: {},
sideEffects: false,
external: false,
};
},
},
},
// load, transform are similar to resolve, refer to their types
],
});
If you want to pass args to your plugins,you can use a closure.
// 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" })],
});
- See Create Plugin to create a new plugin quickly based on official plugin templates.
- This document only covers how to create, develop and publish a js plugin, for more detail about the plugin hooks, see Js Plugin Hooks.
Conventions​
For farm specific js plugins:
- The Farm Js plugin should have a name with a
farm-plugin-
prefix and clear semantics. - Include the
farm-plugin-
keyword in package.json.
If your plugin is only applicable to a specific framework, its name should follow the following prefix format:
farm-plugin-vue-
: Prefix as a Vue pluginfarm-plugin-react-
: Prefix as a React pluginfarm-plugin-svelte-
: Prefix as a svelte plugin- ...
Concepts​
Before you start to write your js plugin, you should know the following concepts:
- filters: Cause Js Plugins are much slower than Rust Plugins, your js plugin need to set explicit filters to avoid unnecessary call for js plugins hook. For example, you should set
transform.filters.moduleTypes = ['js']
to make sure that the transform hook of your js plugin only runs for.js/mjs/cjs
files. - module_type: The type of the module, it can be
js
,ts
,css
,sass
,json
, etc. Farm supportsjs/ts/jsx/tsx
,css
,html
,json
,static assets(png, svg, etc)
natively.module_type
is returned byload
hook ortransform
hook. - resolved_path and module_id:
resolved_path
is the absolute path of the module, andmodule_id
is the unique id of the module, it's usuallyrelative path of the module from the project root
+query
. For example, we import a module asimport './a?query'
, the resolved_path is/project/src/a.ts
and the module_id issrc/a.ts?query
. - context: All the hooks in the plugin accept a
context
argument, it's the compilation context of the farm project, you can use it to get the ModuleGraph, Module, Resources, etc. - Resource and Resource Pot:
Resource
is the final output bundle file, andResource Pot
is the abstract representation of the resource, similar toChunk
of other bundlers. Inside Farm, first we will generateResource Pots
fromModuleGraph
, renderResource Pots
and finally generateResources
fromResource Pots
.
Filters​
Cause Js Plugins
are much slower than Rust Plugins
, Farm use filters
to control the execution of js plugin hooks. The plugin hook executes only when given filters
matched to improve performance. filters
is neccessary for some commonly used hooks, such as resolve
, load
, transform
, etc.
For example, if you want to transform css files, you can use transform.filters.moduleTypes = ['css']
to make sure that the transform hook of your js plugin only runs for .css
files:
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
},
},
};
Module Type​
In Farm, every thing is First Class Citizens
, so Farm designs module_type
to identify the type of a module and handle different kinds of ModuleTypes in different plugins.
module_type
returned by load
hook, and can be transformed by transform
hook. Farm supports js/ts/jsx/tsx
, css
, html
, json
, static assets(png, svg, etc)
natively. For these module types, you can return them directly in load
or transform
hook directly. But if you want to handle custom module types, you may need to implement ohter hooks like parse
, render_resource_pot_modules
, generate resources
, etc to control how to parse, render and generate resources for the custom module types.
Js Plugins don't support parse
, render_resource_pot_modules
, generate resources
hooks, you have to use Rust Plugins to handle custom module types.
Create Plugin​
Farm provides official templates to help your create your js plugins quickly:
- pnpm
- npm
- yarn
pnpm create farm-plugin
npm create farm-plugin@latest
yarn create farm-plugin
then follow the prompts to create your plugin.
or you can create a plugin derectly by running the following command:
- pnpm
- npm
- yarn
pnpm create farm-plugin my-farm-plugin --type js
npm create my-farm-plugin --type js
yarn create my-farm-plugin --type js
Above command will create new js plugin with name my-farm-plugin
in the current directory. --type
can be rust
or js
Develop Plugin​
After creating the plugin, you can start to develop your plugin. The plugin is a pure JavaScript object that defines a set of property hooks:
// 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() {}
// }
};
}
For more detail about the plugin hooks, see Js Plugin Hooks.
Run npm run dev
to compile the plugin and watch for changes. Run npm run build
to build the plugin.
Publish Plugin​
A js plugin package is a normal npm package, you can publish it to npm registry by running npm publish
.