Skip to main content

Js Plugin Api

Farm Js Plugin has designed a similar rollup style design plugin system and easy to migrate your plugins/projects from Rollup/Vite/Webpack.

Configuring Js Plugins

Adding JS plugins by plugins option:

farm.config.ts

Writing Js Plugins

A Farm Js Plugin is a plain javascript object which exposes a set of hooks. for example:

my-farm-plugin.ts
note
  • Farm provided create-farm-plugin tool to help you create and develop you js plugin quickly. For more details about writing JS plugins, refer to Writing JS Plugins

Plugin Hook Overview

The Js plugin hook is the same as the Rust plugin, See Rust Plugin Hook Overview.

note

Not all hooks are exposed to Js Plugins, only hooks listed in this document are available.

hooks

name

  • type: string
  • required: true

The name of this plugins, MUST not be empty.

priority

  • type: number
  • required: false
  • default: 100

The priority of this plugins, default to 100. priority controls the execution order of plugins, the larger the value, the earlier the plugin is executed.

note

Note that the priority of most farm internal plugins like plugin-script, plugin-resolve is 99, which means your plugins is always executed before the internal plugins. If your want to make your plugin executed after farm internal plugins, set priority to a value that smaller than 99, for example: 98. Also the priority value can be negative, you can set it to -9999 to make sure it is always executed at last.

config

  • type: config?: (config: UserConfig) => UserConfig | Promise<UserConfig>;
  • hook type: serial
  • required: false

Modify Farm config in config hook, return the (partial) modified config, the returned config will be deeply merged into the config resolved from cli and config file. You can also directly mutate the config.

Example:

note

config hook is called after all user plugins are resolved, so add new plugins into the config has no effect.

configResolved

  • type: configResolved?: (config: ResolvedUserConfig) => void | Promise<void>;
  • hook type: serial
  • required: false

Called when the config resolved(after all plugin's config hook being called). Useful when you want to get the final resolved config for your plugin.

Example:

configureDevServer

  • type: configureDevServer?: (server: Server) => void | Promise<void>;
  • hook type: serial
  • required: false
note

Note that this hook runs in development mode only.

Called when Dev Server is ready, you can get the dev server instance.

Example:

note

Both config and configResolved hook of js plugin are called before config hook of rust plugin.

configureCompiler

  • type: configureCompiler?: (compiler: Compiler) => void | Promise<void>;
  • hook type: serial
  • required: false

Called when Rust Compiler is ready, this hook runs in both development and production. You can get Compiler instance here

Example:

buildStart

  • type: buildStart?: { executor: Callback<Record<string, never>, void> };
  • hook type: parallel
  • required: false

Called before the compilation starts. You can do some initialization work here.

Example:

note

buildStart is only called once for the first compile. Later compiling like Lazy Compilation and HMR Update won't trigger buildStart.

resolve

  • required: false
  • hook type: first
  • type:
note

All filters sources and importers of resolve hook are regex string.

Custom source resolving from importer, for example, resolving ./b from a.ts:

a.ts

Then the resolve params would be:

The resolve result of default resolver would be:

The HookContext is used to pass status when you can the hooks recursively, for example, your plugin call context.resolve in resolve hook:

In above example, we call context.resolve and pass caller as parameter, then we should add a guard like if (hookContext.caller === 'my-plugin') { to avoid infinite loop.

Note:

  • By default, you resolve hook are executed after the default resolver inside Farm, only the sources that can not be resolved by internal resolver will be passed to your plugin, which means if you want to override the default resolve, you need to set your plugin's priority larger than 101.
  • Usually resolved_path is the real absolute path that points to a file. But you can still return a virtual module id like virtual:my-module, but for virtual module you need to implement load hook to custom how to load your virtual module. And in Farm, resolved_path + query = module_id.
  • ResolveKind presents the import type, Example values: require(imported by commonjs require), cssImport(imported by css's import statement), etc.
  • meta can be shared between plugins and hooks, you can get meta from params of load, transform and parse hooks in any plugin.

load

  • required: false
  • hook type: first
  • type:

Custom how to load your module from a resolved module path or module id. For example, load a virtual module:

module_type and content is required when loading modules in your load hook. source_map is optional, you can return source map if you do transform in the load hook(which is not recommended, we recommend to use transform hook for this situation) or you load original source map from other locations.

filters.resolvedPath of load hook is resolvedPath + query, for example: /root/src/index.vue?vue&type=style&lang=css. If you want to ignore query when filtering modules, you can use $: src/index\\.vue$; If you want to filter modules by query, for example, filtering lang=css, you can use src/index.vue\\.+\\?vue&.+lang=css.

transform

  • required: false
  • hook type: serial
  • type:

Do transformation based on module content and module type. Example for transforming sass to css:

Normal steps for writing transform hook:

  1. add a if guard based moduleType or resolvedPath or moduleId
  2. do transformation of the content
  3. return the transformed content, sourceMap and moduleType

For ignorePreviousSourceMap, if you handled param.sourceMapChain and collapsed the source maps of previous plugins in the transform hook. You should set ignorePreviousSourceMap to true to ensure source map is correct. Otherwise, you should always set this option to false and leave source map chain handled by Farm.

For filters:

  • When both resolvedPaths and moduleTypes are specified, take the union.
  • filters.resolvedPaths is resolvedPath + query, for example: /root/src/index.vue?vue&type=style&lang=css. If you want to ignore query when filtering modules, you can use $: src/index\\.vue$; If you want to filter modules by query, for example, filtering lang=css, you can use src/index.vue\\.+\\?vue&.+lang=css.
  • filters.moduleTypes is NOT regex, it must exactly match the ModuleType like css, js, tsx, etc.
note

transform hook is content to content. There is a similar hook called process_module, process_module is ast to ast. Js plugin does not support process_module hook due to performance issues, if you want ast to ast transformations, try Rust Plugin instead.

buildEnd

  • type: buildEnd?: { executor: Callback<Record<string, never>, void> };
  • hook type: parallel
  • required: false

Called after the ModuleGraph built, but before the resources render and generation starts. You can do some status updating or finalization work here.

Example:

note

buildEnd is only called once for the first compile. Later compiling like Lazy Compilation and HMR Update won't trigger buildEnd.

renderStart

  • type: renderStart?: { executor: Callback<Config['config'], void>; };
  • hook type: parallel
  • required: false

Called before the resources render starts.

Example:

note

renderStart is only called once for the first compile. Later compiling like Lazy Compilation and HMR Update won't trigger renderStart.

renderResourcePot

  • required: false
  • hook type: serial
  • type:

Resource Pot is the abstract representation of the final output bundle file, you can return transformed resourcePot content to mutate the final bundle. For example, rendering css:

We transform all <--layer--> in css resource pot and replace them to real css code.

note

When both filters.moduleIds and filters.resourcePotTypes are specified, take the union.

augmentResourceHash

  • required: false
  • hook type: serial
  • type:

Append resource hash for give Resource Pot. Useful if you want to add additional conditions when generating resource hash.

note

When both filters.moduleIds and filters.resourcePotTypes are specified, take the union.

finalizeResources

  • required: false
  • hook type: serial
  • type:

Do some transformations for all generated resources, return transformed resourcesMap. You can add, remove, modify final generated resources in this hook.

Note:

  • bytes is binary of the final output, for js/css/html code, you can use Buffer.from(bytes).toString() to get the code.
  • name is the final file name.
  • origin represent where this Resource is from, ResourcePot means it's generated from ResourcePot which is a modules bundle; Module means it's from Module, for example, static files like .png/.jpg are from Module.

transformHtml

  • required: false
  • hook type: serial
  • type:

The order is used to configure when to execute transformHtml hook:

  • 0: means pre, executed before parse and generate resources. You can transform original html in this stage.
  • 1 and 2: means normal and post, executed after parse and generate resources. In this stage, all <script>, <link> tag are injected.

Transform the final generated html(after all <script>, <link> tag are injected).

note

You should modify bytes field of htmlResource and return the mutated htmlResource, mutate any other fields take no effects

writeResources

  • required: false
  • hook type: serial
  • type:

Called AFTER all resources are written to disk.

pluginCacheLoaded

  • required: false
  • hook type: serial
  • type:

Extend persistent cache loading for your plugin.

When Persistent Cache enabled, load and transform hook may be skipped when hitting cache. If your plugin relies on previous compilation result(for example, load a virtual module based on existing modules), you may need to implement this hook to load cached infos of your plugin to ensure cache work as expected.

Example:

note

You must decide how to serialize/deserialize cache to bytes in your plugins. For a basic example, you can deserialize data by [...Buffer.from(JSON.stringify(data))]

writePluginCache

  • required: false
  • hook type: serial
  • type:

Extend persistent cache writing for your plugin. writePluginCache is often used together with pluginCaceLoaded to read and write persistent cache for plugin. Return the serialized bytes of your data.

Example:

note

You must decide how to serialize/deserialize cache to bytes in your plugins. For a basic example, you can deserialize data by [...Buffer.from(JSON.stringify(data))]

finish

  • type: finish?: { executor: Callback<Record<string, never>, void> };
  • hook type: parallel
  • required: false

Called before the resources render starts.

Example:

note

finish is only called once for the first compile. Later compiling like Lazy Compilation and HMR Update won't trigger finish.

updateModules

  • required: false
  • hook type: serial
  • type:

Called when calling compiler.update(module_paths). Useful to do some operations like clearing previous state or ignore some files when performing HMR.

  • paths is paths that will be recompiled for this update
  • return the new paths, later compilation will update the new returned paths.
Extremely Fast Web Build Tool Written in Rust

Copyright © 2024 Farm Community. Built with Docusaurus.