1"use strict";(self.webpackChunkfarm_docs=self.webpackChunkfarm_docs||[]).push([[8966],{21189:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>u,contentTitle:()=>c,default:()=>m,frontMatter:()=>a,metadata:()=>d,toc:()=>h});var r=s(49214),i=s(36906),o=s(87955),l=s(12790),t=s(71947);const a={},c="Writing Rust Plugins",d={id:"plugins/writing-plugins/rust-plugin",title:"Writing Rust Plugins",description:"Rust plugins are the recommended way to write your plugins cause Rust plugins are much faster and powerful than Js Plugins. A Rust plugin is a struct that implements farmfe_core:",source:"@site/docs/plugins/writing-plugins/rust-plugin.mdx",sourceDirName:"plugins/writing-plugins",slug:"/plugins/writing-plugins/rust-plugin",permalink:"/docs/plugins/writing-plugins/rust-plugin",draft:!1,unlisted:!1,editUrl:"https://github.com/farm-fe/farm-fe.github.io/tree/main/docs/plugins/writing-plugins/rust-plugin.mdx",tags:[],version:"current",frontMatter:{},sidebar:"pluginSidebar",previous:{title:"Overview",permalink:"/docs/plugins/writing-plugins/overview"},next:{title:"Writing JavaScript Plugins",permalink:"/docs/plugins/writing-plugins/js-plugin"}},u={},h=[{value:"Conventions",id:"conventions",level:2},{value:"Concepts",id:"concepts",level:2},{value:"Module Type",id:"module-type",level:3},{value:"Create Plugin",id:"create-plugin",level:2},{value:"Plugin Project Structure",id:"plugin-project-structure",level:2},{value:"Develop Plugin",id:"develop-plugin",level:2},{value:"Handle ModuleType",id:"handle-moduletype",level:3},{value:"Handle Plugin Options",id:"handle-plugin-options",level:3},{value:"Using farm_core In Plugin",id:"using-farm_core-in-plugin",level:3},{value:"Caveats",id:"caveats",level:3},{value:"Using SWC In Plugin",id:"using-swc-in-plugin",level:4},{value:"Choosing Rust toolchain",id:"choosing-rust-toolchain",level:4},{value:"Plugin Compatibility",id:"plugin-compatibility",level:3},{value:"Cross Build",id:"cross-build",level:2},{value:"Publish",id:"publish",level:2},{value:"Examples",id:"examples",level:2},{value:"Define Plugin",id:"define-plugin",level:3},{value:"Implement Plugin Trait",id:"implement-plugin-trait",level:3},{value:"Load <code>.scss</code> File",id:"load-scss-file",level:3},{value:"Transform <code>sass</code> File",id:"transform-sass-file",level:3}];function p(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"writing-rust-plugins",children:"Writing Rust Plugins"})}),"\n",(0,r.jsxs)(n.p,{children:["Rust plugins are the recommended way to write your plugins cause Rust plugins are much ",(0,r.jsx)(n.strong,{children:"faster and powerful"})," than Js Plugins. A Rust plugin is a ",(0,r.jsx)(n.code,{children:"struct"})," that implements ",(0,r.jsx)(n.code,{children:"farmfe_core::plugin::Plugin"})," trait, example:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:'#![deny(clippy::all)]\n\nuse farmfe_core::{config::Config, plugin::Plugin};\n\nuse farmfe_macro_plugin::farm_plugin;\n\n// define your rust plugins\n#[farm_plugin]\npub struct FarmPluginExample {}\n\nimpl FarmPluginExample {\n // a Rust plugin must export a new method that accepts 2 arguments for initialization\u3002\n fn new(config: &Config, options: String) -> Self {\n Self {}\n }\n}\n// Implement Plugin trait to define plugin hooks\nimpl Plugin for FarmPluginExample {\n fn name(&self) -> &str {\n "FarmPluginExample"\n }\n\n // more hooks here\n}\n'})}),"\n",(0,r.jsx)(n.p,{children:"Note for a Rust plugin struct:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["The struct must be ",(0,r.jsx)(n.code,{children:"pub"})," and ",(0,r.jsx)(n.code,{children:"#[farm_plugin]"})," attribute is required."]}),"\n",(0,r.jsxs)(n.li,{children:["The struct must implement ",(0,r.jsx)(n.code,{children:"Plugin"})," trait, and the ",(0,r.jsx)(n.code,{children:"name"})," method must be implemented."]}),"\n",(0,r.jsxs)(n.li,{children:["The struct must export a ",(0,r.jsx)(n.code,{children:"new"})," method that accepts 2 arguments for initialization, the first argument is ",(0,r.jsx)(n.code,{children:"&Config"})," and the second argument is ",(0,r.jsx)(n.code,{children:"String"}),". The ",(0,r.jsx)(n.code,{children:"new"})," method is called when the plugin is loaded, and the ",(0,r.jsx)(n.code,{children:"Config"})," is the farm project config, and the ",(0,r.jsx)(n.code,{children:"String"})," is the plugin options."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["We also provide a Rust plugin example repository: ",(0,r.jsx)(n.a,{href:"https://github.com/farm-fe/rust-plugin-example",children:"farm-rust-plugin-example"}),"."]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["This document only covers how to create, develop and publish a rust plugin, for more detail about the plugin hooks, see ",(0,r.jsx)(n.a,{href:"/docs/api/rust-plugin-api",children:"Plugin Hooks"}),"."]})}),"\n",(0,r.jsx)(n.h2,{id:"conventions",children:"Conventions"}),"\n",(0,r.jsx)(n.p,{children:"For farm specific Rust plugins:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["The Farm plugin should have a name with a ",(0,r.jsx)(n.code,{children:"farm-plugin-"})," prefix and clear semantics."]}),"\n",(0,r.jsxs)(n.li,{children:["Include the ",(0,r.jsx)(n.code,{children:"farm-plugin-"})," keyword in package.json."]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"If your plugin is only applicable to a specific framework, its name should follow the following prefix format:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"farm-plugin-vue-"}),": Prefix as a Vue plugin"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"farm-plugin-react-"}),": Prefix as a React plugin"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"farm-plugin-svelte-"}
1),": Prefix as a svelte plugin"]}),"\n",(0,r.jsx)(n.li,{children:"..."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"concepts",children:"Concepts"}),"\n",(0,r.jsx)(n.p,{children:"Before you start to write your rust plugin, you should know the following concepts:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"module_type"}),": The type of the module, it can be ",(0,r.jsx)(n.code,{children:"js"}),", ",(0,r.jsx)(n.code,{children:"ts"}),", ",(0,r.jsx)(n.code,{children:"css"}),", ",(0,r.jsx)(n.code,{children:"sass"}),", ",(0,r.jsx)(n.code,{children:"json"}),", etc. Farm supports ",(0,r.jsx)(n.code,{children:"js/ts/jsx/tsx"}),", ",(0,r.jsx)(n.code,{children:"css"}),", ",(0,r.jsx)(n.code,{children:"html"}),", ",(0,r.jsx)(n.code,{children:"json"}),", ",(0,r.jsx)(n.code,{children:"static assets(png, svg, etc)"})," natively. ",(0,r.jsx)(n.code,{children:"module_type"})," is returned by ",(0,r.jsx)(n.code,{children:"load"})," hook. You can extend natively supported module type by Rust plugins the same as Farm internal plugins."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"resolved_path and module_id"}),": ",(0,r.jsx)(n.code,{children:"resolved_path"})," is the absolute path of the module, and ",(0,r.jsx)(n.code,{children:"module_id"})," is the unique id of the module, it's usually ",(0,r.jsx)(n.code,{children:"relative path of the module from the project root"})," + ",(0,r.jsx)(n.code,{children:"query"}),". For example, we import a module as ",(0,r.jsx)(n.code,{children:"import './a?query'"}),", the resolved_path is ",(0,r.jsx)(n.code,{children:"/project/src/a.ts"})," and the module_id is ",(0,r.jsx)(n.code,{children:"src/a.ts?query"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"context"}),": All the hooks in the plugin accept a ",(0,r.jsx)(n.code,{children:"context"})," argument, it's the compilation context of the farm project, you can use it to get the ModuleGraph, Module, Resources, etc."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Resource and Resource Pot"}),": ",(0,r.jsx)(n.code,{children:"Resource"})," is the final output bundle file, and ",(0,r.jsx)(n.code,{children:"Resource Pot"})," is the abstract representation of the resource, similar to ",(0,r.jsx)(n.code,{children:"Chunk"})," of other bundlers. Inside Farm, first we will generate ",(0,r.jsx)(n.code,{children:"Resource Pots"})," from ",(0,r.jsx)(n.code,{children:"ModuleGraph"}),", render ",(0,r.jsx)(n.code,{children:"Resource Pots"})," and finally generate ",(0,r.jsx)(n.code,{children:"Resources"})," from ",(0,r.jsx)(n.code,{children:"Resource Pots"}),"."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"module-type",children:"Module Type"}),"\n",(0,r.jsxs)(n.p,{children:["In Farm, every thing is ",(0,r.jsx)(n.code,{children:"First Class Citizens"}),", so Farm designs ",(0,r.jsx)(n.code,{children:"module_type"})," to identify the type of a module and handle different kinds of ModuleTypes in different plugins."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"module_type"})," returned by ",(0,r.jsx)(n.code,{children:"load"})," hook, and can be transformed by ",(0,r.jsx)(n.code,{children:"transform"})," hook. Farm supports ",(0,r.jsx)(n.code,{children:"js/ts/jsx/tsx"}),", ",(0,r.jsx)(n.code,{children:"css"}),", ",(0,r.jsx)(n.code,{children:"html"}),", ",(0,r.jsx)(n.code,{children:"json"}),", ",(0,r.jsx)(n.code,{children:"static assets(png, svg, etc)"})," natively. For these module types, you can return them directly in ",(0,r.jsx)(n.code,{children:"load"})," or ",(0,r.jsx)(n.code,{children:"transform"})," hook directly. But if you want to handle custom module types, you may need to implement ohter hooks like ",(0,r.jsx)(n.code,{children:"parse"}),", ",(0,r.jsx)(n.code,{children:"render_resource_pot_modules"}),", ",(0,r.jsx)(n.code,{children:"generate resources"}),", etc to control how to parse, render and generate resources for the custom module types."]}),"\n",(0,r.jsx)(n.h2,{id:"create-plugin",children:"Create Plugin"}),"\n",(0,r.jsx)(n.p,{children:"Farm provides official templates to help your create your rust plugins quickly:"}),"\n",(0,r.jsx)(r.Fragment,{children:(0,r.jsxs)(l.A,{children:[(0,r.jsx)(t.A,{value:"pnpm",label:"pnpm",children:(0,r.jsx)(o.A,{children:"pnpm create farm-plugin"})}),(0,r.jsx)(t.A,{value:"npm",label:"npm",children:(0,r.jsx)(o.A,{children:"npm create farm-plugin@latest"})}),(0,r.jsx)(t.A,{value:"yarn",label:"yarn",children:(0,r.jsx)(o.A,{children:"yarn create farm-plugin"})})]})}),"\n",(0,r.jsx)(n.p,{children:"then follow the prompts to create your plugin."}),"\n",(0,r.jsx)(n.p,{children:"or you can create a plugin derectly by running the following command:"}),"\n",(0,r.jsxs)(l.A,{children:[(0,r.jsx)(t.A,{value:"pnpm",label:"pnpm",children:(0,r.jsx)(o.A,{children:"pnpm create farm-plugin my-farm-plugin --type rust"})}),(0,r.jsx)(t.A,{value:"npm",label:"npm",children:(0,r.jsx)(o.A,{children:"npm create my-farm-plugin --type rust"})}),(0,r.jsx)(t.A,{value:"yarn",label:"yarn",children:(0,r.jsx)(o.A,{children:"yarn create my-farm-plugin --type rust"})})]}),"\n",(0,r.jsxs)(n.p,{children:["Above command will create new rust plugin with name ",(0,r.jsx)(n.code,{children:"my-farm-plugin"})," in the current directory. ",(0,r.jsx)(n.code,{children:"--type"})," can be ",(0,r.jsx)(n.code,{children:"rust"})," or ",(0,r.jsx)(n.code,{children:"js"})]}),"\n",(0,r.jsx)(n.h2,{id:"plugin-project-structure",children:"Plugin Project Structure"}),"\n",(0,r.jsx)(n.p,{children:"The plugin project structure is as follows:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-plaintext",children:"my-farm-plugin\n\u251c\u2500\u2500 .github\n\u2502 \u2514\u2500\u2500 workflows\n| \u251c\u2500\u2500 release.yml\n| \u251c\u2500\u2500 build.yml\n\u2502 \u2514\u2500\u2500 ci.yml\n\u251c\u2500\u2500 Cargo.toml\n|\u2500\u2500 .gitignore\n\u251c\u2500\u2500 npm\n\u2502 \u251c\u2500\u2500 darwin-x64\n\u2502 \u251c\u2500\u2500 linux-x64-gnu\n| \u251c\u2500\u2500 win32-x64-msvc\n\u2502 \u2514\u2500\u2500 ...\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 src\n\u2502 \u2514\u2500\u2500 lib.rs\n\u2514\u2500\u2500 rust-toolchain.toml\n"})}),"\n",(0,r.jsx)(n.p,{children:"Notable files and directories:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"src/lib.rs"}),": The main file of the plugin, where you define your plugin."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"Cargo.toml"}),": The manifest file for Rust."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"package.json"}),": The manifest file for npm."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"npm"}),": Where your platform specific binary packages placed. These packages should be published to npm registry before publish the plugin."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".github/workflows"}),": Used to cross build and publish your plugin in github actions."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"rust-toolchain.toml"}),": The rust toolchain file, it should ",(0,r.jsx)(n.strong,{children:"not be modified manually"}),", it should always using ",(0,r.jsx)(n.strong,{children:"the same version as the farm core"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Farm provides a tool(",(0,r.jsx)(n.code,{children:"@farmfe/plugin-tools"}),") to help you build and publish your rust plugin, see ",(0,r.jsx)(n.code,{children:"package.json"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-json",children:'{\n // ...\n "scripts": {\n // build your plugin for current platform\n "build": "farm-plugin-tools build --platform --cargo-name my_farm_plugin -p my_farm_plugin --release",\n // publish all platform packages under npm directory to npm registry\n "prepublishOnly": "farm-plugin-tools prepublish"\n },\n // ...\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["More detail about building and publishing your plugin, see ",(0,r.jsx)(n.a,{href:"#cross-build",children:"buidling"})," and ",(0,r.jsx)(n.a,{href:"#publish",children:"publishing"})," sections."]}),"\n",(0,r.jsx)(n.h2,{id:"develop-plugin",children:"Develop Plugin"}),"\n",(0,r.jsx)(n.p,{children:"To develop and test your plugin locally, you should build your plugin for your platform first, run:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"pnpm build\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Then you can use the built plugin in your farm project by adding the plugin to the ",(0,r.jsx)(n.code,{children:"plugins"})," field in ",(0,r.jsx)(n.code,{children:"farm.config.ts"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"import { defineConfig } from '@farmfe/core';
1\n\nexport default defineConfig({\n plugins: [\n 'my-farm-plugin'\n ]\n});\n"})}),"\n",(0,r.jsxs)(n.p,{children:["and execute ",(0,r.jsx)(n.code,{children:"pnpm i"})," in your farm project, and run ",(0,r.jsx)(n.code,{children:"farm start"})," to start your farm project with your plugin."]}),"\n",(0,r.jsxs)(n.p,{children:["when you make changes to your plugin, you should rebuild your plugin and restart your farm project to see the changes. for example, add ",(0,r.jsx)(n.code,{children:"load"})," hook to your plugin:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:'{8-18} title="src/lib.rs"',children:'// ... ignore other code\n\nimpl Plugin for FarmPluginExample {\n fn name(&self) -> &str {\n "FarmPluginExample"\n }\n\n fn load(\n &self,\n param: &farmfe_core::plugin::PluginLoadHookParam,\n _context: &std::sync::Arc<farmfe_core::context::CompilationContext>,\n _hook_context: &farmfe_core::plugin::PluginHookContext,\n ) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginLoadHookResult>> {\n println!(\n "load path: {:?}, id: {:?}",\n param.resolved_path, param.module_id\n );\n Ok(None)\n }\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["Then rebuild your plugin with ",(0,r.jsx)(n.code,{children:"pnpm build"})," and restart your farm project with ",(0,r.jsx)(n.code,{children:"farm start"}),", you will see the ",(0,r.jsx)(n.code,{children:"load"})," hook is called when compiling your farm project."]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["For more detail about the plugin hooks, see ",(0,r.jsx)(n.a,{href:"/docs/api/rust-plugin-api",children:"Plugin Hooks"}),"."]})}),"\n",(0,r.jsx)(n.h3,{id:"handle-moduletype",children:"Handle ModuleType"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"module_type"})," is returned by the ",(0,r.jsx)(n.code,{children:"load"})," hook or ",(0,r.jsx)(n.code,{children:"transform"})," hook. Your set any module type to the module in the ",(0,r.jsx)(n.code,{children:"load"})," hook, and the module will be processed by the corresponding plugin that supports the module type."]}),"\n",(0,r.jsxs)(n.p,{children:["For native supported module types, you can just return the module type in the ",(0,r.jsx)(n.code,{children:"load"})," hook:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:'{8-18} title="src/lib.rs"',children:'// ... ignore other code\n\nimpl Plugin for FarmPluginExample {\n fn name(&self) -> &str {\n "FarmPluginExample"\n }\n\n fn load(\n &self,\n param: &farmfe_core::plugin::PluginLoadHookParam,\n _context: &std::sync::Arc<farmfe_core::context::CompilationContext>,\n _hook_context: &farmfe_core::plugin::PluginHookContext,\n ) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginLoadHookResult>> {\n // handle virtual module\n if param.module_id.starts_with("virtual:my-css:css") {\n // return module type and content\n Ok(Some(farmfe_core::plugin::PluginLoadHookResult {\n module_type: "css".to_string(),\n content: ".red { color: red; }".to_string(),\n ..Default::default()\n }))\n } else {\n Ok(None)\n }\n }\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["For non-native supported module types, you should use ",(0,r.jsx)(n.code,{children:"transform"})," hook to transform the module type to a native supported module type, otherwise you need to implement ",(0,r.jsx)(n.code,{children:"parse"}),", ",(0,r.jsx)(n.code,{children:"renderResourcePot"})," hook to handle your custom module type:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:'{8-18} title="src/lib.rs"',children:'// ... ignore other code\n\nimpl Plugin for FarmPluginExample {\n fn name(&self) -> &str {\n "FarmPluginExample"\n }\n\n fn transform(\n &self,\n param: &farmfe_core::plugin::PluginTransformHookParam,\n _context: &std::sync::Arc<farmfe_core::context::CompilationContext>,\n _hook_context: &farmfe_core::plugin::PluginHookContext,\n ) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginTransformHookResult>> {\n // module type guard is required\n if matches!(param.module_type, ModuleType::Custom("sass")) {\n // compile sass and transform the module type from sass to css\n Ok(Some(farmfe_core::plugin::PluginTransformHookResult {\n module_type: "css".to_string(),\n content: compileSass(param.content),\n ..Default::default()\n }))\n } else {\n Ok(None)\n }\n }\n}\n'})}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["Module type guard like ",(0,r.jsx)(n.code,{children:'matches!(param.module_type, ModuleType::Custom("sass"))'})," is required in the ",(0,r.jsx)(n.code,{children:"transform"})," hook, cause the ",(0,r.jsx)(n.code,{children:"transform"})," hook will be called for all module types, and you should only handle your custom module type in the ",(0,r.jsx)(n.code,{children:"transform"})," hook. So do the ",(0,r.jsx)(n.code,{children:"parse"})," and other hooks."]})}),"\n",(0,r.jsxs)(n.p,{children:["or implement ",(0,r.jsx)(n.code,{children:"parse"}),", ",(0,r.jsx)(n.code,{children:"render_resource_pot_modules"})," hook to handle your custom module type, see how native farm css plugin handle ",(0,r.jsx)(n.code,{children:"css"})," module type in ",(0,r.jsx)(n.a,{href:"https://github.com/farm-fe/farm/blob/main/crates/plugin_html/src/lib.rs#L159",children:"farm-plugin-css"}),"."]}),"\n",(0,r.jsx)(n.h3,{id:"handle-plugin-options",children:"Handle Plugin Options"}),"\n",(0,r.jsxs)(n.p,{children:["The rust plugin options can be configured in ",(0,r.jsx)(n.code,{children:"farm.config.ts"}),":"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-javascript",children:"import { defineConfig } from '@farmfe/core';
1\n\nexport default defineConfig({\n plugins: [\n ['my-farm-plugin', {\n // plugin options\n myOption: 'myOption'\n }]\n ]\n});\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The Option will be json serialized and passed to the ",(0,r.jsx)(n.code,{children:"new"})," method of your plugin, you can handle the options in the ",(0,r.jsx)(n.code,{children:"new"})," method:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:'title="src/lib.rs"',children:"// ... ignore other code\n\n// define your rust plugin options\n#[derive(serde::Deserialize)]\npub struct Options {\n pub my_option: Option<String>,\n}\n\nimpl FarmPluginExample {\n fn new(config: &Config, options: String) -> Self {\n // deserialize the options\n let my_option: Options = serde_json::from_str(&options).unwrap();\n // handle the options...\n Self {}\n }\n}\n"})}),"\n",(0,r.jsxs)(n.p,{children:["Note that you should add dependencies ",(0,r.jsx)(n.code,{children:"serde"})," and ",(0,r.jsx)(n.code,{children:"serde_json"})," to your ",(0,r.jsx)(n.code,{children:"Cargo.toml"})," to support options deserialization:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-toml",children:'[depen
1dencies]\n# ... ignore other code\nserde = { version = "1.0", features = ["derive"] }\nserde_json = "1.0"\n'})}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["Non json serializable options are not supported. Which means you can only use types like string, number, boolean, array, object, etc. ",(0,r.jsx)(n.code,{children:"function options"})," are not supported."]})}),"\n",(0,r.jsx)(n.h3,{id:"using-farm_core-in-plugin",children:"Using farm_core In Plugin"}),"\n",(0,r.jsxs)(n.p,{children:["Farm exposes all core structures and utilities in ",(0,r.jsx)(n.a,{href:"https://docs.rs/farmfe_core",children:(0,r.jsx)(n.code,{children:"farmfe_core"})})," crate. Refer to the ",(0,r.jsx)(n.a,{href:"https://docs.rs/farmfe_core",children:"farmfe_core"})," documentation for more detail."]}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsxs)(n.p,{children:["If you want to use swc structures like ",(0,r.jsx)(n.code,{children:"Module"}),", ",(0,r.jsx)(n.code,{children:"Program"}),", etc. in your plugin, you should use ",(0,r.jsx)(n.code,{children:"farmfe_core::swc_ast"})," that re-exposed by farm core. Cause the swc version used by farm core may be different from the swc version you used in your plugin, and the swc version used by farm core is guaranteed to be compatible with farm core."]})}),"\n",(0,r.jsx)(n.h3,{id:"caveats",children:"Caveats"}),"\n",(0,r.jsx)(n.h4,{id:"using-swc-in-plugin",children:"Using SWC In Plugin"}),"\n",(0,r.jsxs)(n.p,{children:["Note that your rust plugin should not use any SWC related packages like ",(0,r.jsx)(n.code,{children:"swc_common"}),", ",(0,r.jsx)(n.code,{children:"swc_transforms"}),", etc. Cause SWC stores the global state in the process, it may cause ",(0,r.jsx)(n.strong,{children:"dead lock"})," when you use SWC in your plugin."]}),"\n",(0,r.jsxs)(n.p,{children:["Farm recommended to write ",(0,r.jsx)(n.a,{href:"/docs/using-plugins#using-swc-plugins",children:"SWC Plugin"})," if you want to make changes to the AST of your farm project. For how to write SWC plugin, see ",(0,r.jsx)(n.a,{href:"https://swc.rs/docs/plugin/ecmascript/getting-started",children:"Write SWC Plugin"}),"."]}),"\n",(0,r.jsx)(n.h4,{id:"choosing-rust-toolchain",children:"Choosing Rust toolchain"}),"\n",(0,r.jsxs)(n.p,{children:["Cause Farm Rust Plugin is a dynamic linked library, you should always use the same version of the rust toolchain as the farm core. The rust toolchain is defined in ",(0,r.jsx)(n.code,{children:"rust-toolchain.toml"}),", it should ",(0,r.jsx)(n.strong,{children:"not be modified manually"}),".\nAnd should should always build your plugin from Rust, cause Farm Core does not support FFI and not promise ABI stability to provide best performance."]}),"\n",(0,r.jsx)(n.h3,{id:"plugin-compatibility",children:"Plugin Compatibility"}),"\n",(0,r.jsxs)(n.p,{children:["Farm core maintains a API version that exposes to the plugin. If you met a message like ",(0,r.jsx)(n.code,{children:"Incompatible Rust Plugin: Current core's version..."}),", it means your plugin is not compatible with the current farm core version. You should update your plugin to the latest version to fix the issue."]}),"\n",(0,r.jsx)(n.p,{children:"For plugin authors, you should rebuild and publish your plugin for the latest farm core version to make your plugin compatible with the latest farm core version."}),"\n",(0,r.jsx)(n.admonition,{type:"note",children:(0,r.jsx)(n.p,{children:"Farm promises API Compatibility for the same major version, for example, if your plugin is compatible with farm core 1.0.0, it should also be c
1ompatible with farm core 1.1.0, 1.2.0, etc. which means your plugin will always work for the same major version of farm."})}),"\n",(0,r.jsx)(n.h2,{id:"cross-build",children:"Cross Build"}),"\n",(0,r.jsxs)(n.p,{children:["A Farm Rust Plugin is a ",(0,r.jsx)(n.strong,{children:"platform specific dynamic linked library"}),", you should build your plugin for all platforms you want to support.\nFarm provided a example for how to build your plugin using github actions, see ",(0,r.jsx)(n.a,{href:"https://github.com/farm-fe/rust-plugin-example/blob/main/.github/workflows/build.yaml",children:".github/workflows/build.yml"})]}),"\n",(0,r.jsx)(n.p,{children:"By default, A farm rust plugin should be built for the following platforms:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"linux-x64-gnu "})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"linux-x64-musl"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"darwin-x64"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"win32-x64-msvc"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"linux-arm64-musl"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"linux-arm64-gnu"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"darwin-arm64"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"win32-ia32-msvc"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"win32-arm64-msvc"})}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"For a public plugin that published to npm registry, we recommend you to publish your plugin for all platforms above. For a private rust plugin, you can build your plugin for any platform you want to support."}),"\n",(0,r.jsx)(n.admonition,{type:"tip",children:(0,r.jsx)(n.p,{children:"Cause a rust plugin is a pure dynamic linked library, if you have questions about how to build your plugin for a specific platform, just google how to build a dynamic linked library for that platform in Rust."})}),"\n",(0,r.jsx)(n.h2,{id:"publish",children:"Publish"}),"\n",(0,r.jsx)(n.p,{children:"Steps to publish your Rust plugin:"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Cross build the Rust plugin to dynamic linked library, see ",(0,r.jsx)(n.a,{href:"#cross-build",children:"Cross Build"})," for detail."]}),"\n",(0,r.jsxs)(n.li,{children:["Copy the binary artifacts to npm dir, for example: Copy to ",(0,r.jsx)(n.code,{children:"npm/linux-x64-gnu/index.farm"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["Publish platform specific packages under npm dir, you can use ",(0,r.jsx)(n.code,{children:"farm-plugin-tool prepublish"})," to publish packages under ",(0,r.jsx)(n.code,{children:"npm"})," dir."]}),"\n",(0,r.jsx)(n.li,{children:"Publish the package itself"}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["see example ",(0,r.jsx)(n.a,{href:"https://github.com/farm-fe/rust-plugin-example/blob/main/.github/workflows/release.yml",children:"github actions publish workflow"})]}),"\n",(0,r.jsx)(n.h2,{id:"examples",children:"Examples"}),"\n",(0,r.jsxs)(n.p,{children:["We will use ",(0,r.jsx)(n.code,{children:"@farmfe/plugin-sass"})," as demostration to a real Rust plugin example. This plugin will support compiling ",(0,r.jsx)(n.code,{children:".scss"})," and ",(0,r.jsx)(n.code,{children:".sass"})," file in your farm project."]}),"\n",(0,r.jsx)(n.h3,{id:"define-plugin",children:"Define Plugin"}),"\n",(0,r.jsxs)(n.p,{children:["Exports a Rust struct named ",(0,r.jsx)(n.code,{children:"FarmPluginSass"}),"."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:'title="src/lib.rs"',children:'use farmfe_macro_plugin::farm_plugin;\n\n// 1. define a struct with #[farm_plugin] attribute\n#[farm_plugin]\npub struct FarmPluginSass {\n sass_options: String,\n regex: Regex,\n}\n\nimpl FarmPluginSass {\n // 2. define a new method with 2 arguments\n pub fn new(_config: &Config, options: String) -> Self {\n Self {\n sass_options: options,\n regex: Regex::new(r#"\\.(sass|scss)$"#).unwrap(),\n }\n }\n}\n'})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["The struct must be ",(0,r.jsx)(n.code,{children:"pub"})," and ",(0,r.jsx)(n.code,{children:"#[farm_plugin]"})," attribute is required."]}),"\n",(0,r.jsxs)(n.li,{children:["The struct must export a ",(0,r.jsx)(n.code,{children:"new"})," method that accepts 2 arguments for initialization, the first argument is ",(0,r.jsx)(n.code,{children:"&Config"})," and the second argument is ",(0,r.jsx)(n.code,{children:"String"}),"."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"implement-plugin-trait",children:"Implement Plugin Trait"}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"Plugin"})," trait is used to define ",(0,r.jsx)(n.code,{children:"hooks"})," that can hook into Farm compiler."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:"{21-30}",children:'use farmfe_core::plugin::Plugin;\nuse farmfe_macro_plugin::farm_plugin;\n\n// 1. define a struct with #[farm_plugin] attribute\n#[farm_plugin]\npub struct FarmPluginSass {\n sass_options: String,\n regex: Regex,\n}\n\nimpl FarmPluginSass {\n // 2. define a new method with 2 arguments\n pub fn new(_config: &Config, options: String) -> Self {\n Self {\n sass_options: options,\n regex: Regex::new(r#"\\.(sass|scss)$"#).unwrap(),\n }\n }\n}\n// Implement Plugin Trait\nimpl Plugin for FarmPluginSass {\n fn n
1ame(&self) -> &str {\n "FarmPluginSass"\n }\n\n // this plugin should be executed before internal plugins\n fn priority(&self) -> i32 {\n 101\n }\n}\n'})}),"\n",(0,r.jsxs)(n.h3,{id:"load-scss-file",children:["Load ",(0,r.jsx)(n.code,{children:".scss"})," File"]}),"\n",(0,r.jsxs)(n.p,{children:["Implement ",(0,r.jsx)(n.code,{children:"load"})," hook to support load ",(0,r.jsx)(n.code,{children:".scss"})," files."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",metastring:"{14-32}",children:'// ignore other code ...\n\n// Implement Plugin Trait\nimpl Plugin for FarmPluginSass {\n fn name(&self) -> &str {\n "FarmPluginSass"\n }\n\n // this plugin should be executed before internal plugins\n fn priority(&self) -> i32 {\n 101\n }\n\n fn load(\n &self,\n param: &farmfe_core::plugin::PluginLoadHookParam,\n _context: &std::sync::Arc<farmfe_core::context::CompilationContext>,\n _hook_context: &farmfe_core::plugin::PluginHookContext,\n ) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginLoadHookResult>> {\n if param.query.is_empty() && self.regex.is_match(param.resolved_path) {\n let content = fs::read_file_utf8(param.resolved_path);\n\n if let Ok(content) = content {\n return Ok(Some(farmfe_core::plugin::PluginLoadHookResult {\n content,\n module_type: ModuleType::Custom(String::from("sass")),\n }));\n }\n }\n\n Ok(None)\n }\n}\n'})}),"\n",(0,r.jsxs)(n.p,{children:["In the ",(0,r.jsx)(n.code,{children:"load"})," hook, we only read the file that ends with ",(0,r.jsx)(n.code,{children:".scss"})," or ",(0,r.jsx)(n.code,{children:".sass"}),", return the file content and maked its module_type as ",(0,r.jsx)(n.code,{children:'ModuleType::Custom(String::from("sass"))'}),"."]}),"\n",(0,r.jsxs)(n.h3,{id:"transform-sass-file",children:["Transform ",(0,r.jsx)(n.code,{children:"sass"})," File"]}),"\n",(0,r.jsxs)(n.p,{children:["After we load the ",(0,r.jsx)(n.code,{children:".scss"})," file, we need to transform it to ",(0,r.jsx)(n.code,{children:"css"})," in ",(0,r.jsx)(n.code,{children:"transform"})," hook, then Farm will treat it as css in following process."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:'// ignore other code ...\nfn transform(\n &self,\n param: &farmfe_core::plugin::PluginTransformHookParam,\n context: &std::sync::Arc<farmfe_core::context::CompilationContext>,\n) -> farmfe_core::error::Result<Option<farmfe_core::plugin::PluginTransformHookResult>> {\n // module type guard is neccessary\n if param.module_type == ModuleType::Custom(String::from("sass")) {\n // ... ignore other code\n\n // parse options\n const options = parse_options(&self.options, param.module_id);\n // compile sass to css\n let compile_result = compileSass(¶m.content, options);\n\n return Ok(Some(farmfe_core::plugin::PluginTransformHookResult {\n content: compile_result.css,\n source_map: compile_result.source_map,\n // tell farm compiler that we have transformed this module to css\n module_type: Some(farmfe_core::module::ModuleType::Css),\n ignore_previous_source_map: false,\n }));\n }\n\n Ok(None)\n}\n'})}),"\n",(0,r.jsx)(n.admonition,{type:"tip",children:(0,r.jsxs)(n.p,{children:["This example only covers how to implement a transformer plugin. For more abilities that Farm support, refer to ",(0,r.jsx)(n.a,{href:"/docs/api/rust-plugin-api",children:"Plugin Hooks"}),"."]})})]})}function m(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(p,{...e})}):p(e)}},87955:(e,n,s)=>{s.d(n,{A:()=>c});var r=s(48318);const i={"shiki-wrapper":"shiki-wrapper_eFVS","code-header":"code-header_obPU","code-title":"code-title_n3kD","copy-button":"copy-button_MpeX","copy-icon":"copy-icon_DvHB",popIn:"popIn_sXIp","copy-success":"copy-success_xuS0"};var o=s(70251),l=s(21970);const t=[{name:"ts",content:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="#3178C6" d="M20 0h216c11.046 0 20 8.954 20 20v216c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20V20C0 8.954 8.954 0 20 0Z"/><path fill="#FFF" d="M150.518 200.475v27.62c4.492 2.302 9.805 4.028 15.938 5.179c6.133 1.151 12.597 1.726 19.393 1.726c6.622 0 12.914-.633 18.874-1.899c5.96-1.266 11.187-3.352 15.678-6.257c4.492-2.906 8.048-6.704 10.669-11.394c2.62-4.689 3.93-10.486 3.93-17.391c0-5.006-.749-9.394-2.246-13.163a30.748 30.748 0 0 0-6.479-10.055c-2.821-2.935-6.205-5.567-10.149-7.898c-3.945-2.33-8.394-4.531-13.347-6.
1602c-3.628-1.497-6.881-2.949-9.761-4.359c-2.879-1.41-5.327-2.848-7.342-4.316c-2.016-1.467-3.571-3.021-4.665-4.661c-1.094-1.64-1.641-3.495-1.641-5.567c0-1.899.489-3.61 1.468-5.135s2.362-2.834 4.147-3.927c1.785-1.094 3.973-1.942 6.565-2.547c2.591-.604 5.471-.906 8.638-.906c2.304 0 4.737.173 7.299.518c2.563.345 5.14.877 7.732 1.597a53.669 53.669 0 0 1 7.558 2.719a41.7 41.7 0 0 1 6.781 3.797v-25.807c-4.204-1.611-8.797-2.805-13.778-3.582c-4.981-.777-10.697-1.165-17.147-1.165c-6.565 0-12.784.705-18.658 2.115c-5.874 1.409-11.043 3.61-15.506 6.602c-4.463 2.993-7.99 6.805-10.582 11.437c-2.591 4.632-3.887 10.17-3.887 16.615c0 8.228 2.375 15.248 7.127 21.06c4.751 5.811 11.963 10.731 21.638 14.759a291.458 291.458 0 0 1 10.625 4.575c3.283 1.496 6.119 3.049 8.509 4.66c2.39 1.611 4.276 3.366 5.658 5.265c1.382 1.899 2.073 4.057 2.073 6.474a9.901 9.901 0 0 1-1.296 4.963c-.863 1.524-2.174 2.848-3.93 3.97c-1.756 1.122-3.945 1.999-6.565 2.632c-2.62.633-5.687.95-9.2.95c-5.989 0-11.92-1.05-17.794-3.151c-5.875-2.1-11.317-5.25-16.327-9.451Zm-46.036-68.733H140V109H41v22.742h35.345V233h28.137V131.742Z"/></svg>'},{name:"js",content:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="#F7DF1E" d="M0 0h256v256H0V0Z"/><path d="m67.312 213.932l19.59-11.856c3.78 6.701 7.218 12.371 15.465 12.371c7.905 0 12.89-3.092 12.89-15.12v-81.798h24.057v82.138c0 24.917-14.606 36.259-35.916 36.259c-19.245 0-30.416-9.967-36.087-21.996m85.07-2.576l19.588-11.341c5.157 8.421 11.859 14.607 23.715 14.607c9.969 0 16.325-4.984 16.325-11.858c0-8.248-6.53-11.17-17.528-15.98l-6.013-2.58c-17.357-7.387-28.87-16.667-28.87-36.257c0-18.044 13.747-31.792 35.228-31.792c15.294 0 26.292 5.328 34.196 19.247l-18.732 12.03c-4.125-7.389-8.591-10.31-15.465-10.31c-7.046 0-11.514 4.468-11.514 10.31c0 7.217 4.468 10.14 14.778 14.608l6.014 2.577c20.45 8.765 31.963 17.7 31.963 37.804c0 21.654-17.012 33.51-39.867 33.51c-22.339 0-36.774-10.654-43.819-24.574"/></svg>'},{name:"css",content:'<svg xmlns="http://www.w3.org/2000/svg" width="29.37" height="32" viewBox="0 0 256 279"><defs><linearGradient id="logosWindiCss0" x1="16.639%" x2="137.275%" y1="36.186%" y2="175.371%"><stop offset="7.822%" stop-color="#54C8F1"/><stop offset="57.292%" stop-color="#41A2EF"/><stop offset="100%" stop-color="#3386EE"/></linearGradient><linearGradient id="logosWindiCss1" x1="-22.099%" x2="91.451%" y1="-49.621%" y2="73.695%"><stop offset="7.822%" stop-color="#54C8F1"/><stop offset="57.292%" stop-color="#41A2EF"/><stop offset="100%" stop-color="#3386EE"/></linearGradient><linearGradient id="logosWindiCss2" x1="8.696%" x2="71.739%" y1="37.461%" y2="80.271%"><stop offset="7.822%" stop-color="#54C8F1"/><stop offset="57.292%" stop-color="#41A2EF"/><stop offset="100%" stop-color="#3386EE"/></linearGradient><linearGradient id="logosWindiCss3" x1="60.744%" x2="501.135%" y1="-308.636%" y2="225.788%"><stop offset="7.822%" stop-color="#54C8F1"/><stop offset="57.292%" stop-color="#41A2EF"/><stop offset="100%" stop-color="#3386EE"/></linearGradient></defs><path fill="url(#logosWindiCss0)" d="M133.784 48.922C133.784 21.945 111.84 0 84.863 0c-28.43 0-45.428 21.316-45.428 41.933h27.956c0-5.8 5.423-13.978 17.472-13.978c11.56 0 20.966 9.407 20.966 20.967c0 11.56-8.438 20.966-29.715 20.966H0v27.955h76.114c35.911 0 57.67-21.944 57.67-48.921Zm0 0C133.784 21.945 111.84 0 84.863 0c-28.43 0-45.428 21.316-45.428 41.933h27.956c0-5.8 5.423-13.978 17.472-13.978c11.56 0 20.966 9.407 20.966 20.967c0 11.56-8.438 20.966-29.715 20.966H0v27.955h76.114c35.911 0 57.67-21.944 57.67-48.921Z"/><path fill="url(#logosWindiCss1)" d="M195.803 229.339c0 26.978-21.945 48.922-48.922 48.922c-28.43 0-45.427-21.315-45.427-41.933h27.955c0 5.8 5.423 13.979 17.472 13.979c11.56 0 20.967-9.408 20.967-20.968s-10.63-20.966-29.716-20.966H53.671v-27.956h84.461c35.912 0 57.671 21.945 57.671 48.922Zm0 0c0 26.978-21.945 48.922-48.922 48.922c-28.43 0-45.427-21.315-45.427-41.933h27.955c0 5.8 5.423 13.979 17.472 13.979c11.56 0 20.967-9.408 20.967-20.968s-10.63-20.966-29.716-20.966H53.671v-27.956h84.461c35.912 0 57.671 21.945 57.671 48.922Z"/><path fill="url(#logosWindiCss2)" d="M203.13 43.13c-30.834 0-52.585 23.45-52.585 54.284h24.76c0-15.327 11.13-26.457 27.825-26.457c16.696 0 25.044 11.13 25.044 26.457s-10.842 27.955-40.348 27.955H0v27.956h192c43.695 0 64-28.085 64-55.911c0-30.835-20.87-54.284-52.87-54.284Z"/><path fill="url(#logosWindiCss3)" d="M36.647 180.417H0v28.19h36.647z"/></svg>'},{name:"json",content:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 16 16"><path fill="none" stroke="#eed49f" stroke-linecap="round" stroke-linejoin="round" d="M4.5 2.5H4c-.75 0-1.5.75-1.5 1.5v2c0 1.1-1 2-1.83 2c.83 0 1.83.9 1.83 2v2c0 .75.75 1.5 1.5 1.5h.5m7-11h.5c.75 0 1.5.75 1.5 1.5v2c0 1.1 1 2 1.83 2c-.83 0-1.83.9-1.83 2v2c0 .74-.75 1.5-1.5 1.5h-.5m-6.5-3a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1m3 0a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1m3 0a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1"/></svg>'},{name:"text",content:'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 48 48"><g fill="none" stroke-width="4"><path fill="#2F88FF" stroke="#000" stroke-linejoin="round" d="M10 4H30L40 14V42C40 43.1046 39.1046 44 38 44H10C8.89543 44 8 43.1046 8 42V6C8 4.89543 8.89543 4 10 4Z"/><path stroke="#fff" stroke-linecap="round" d="M18 18.0083H30"/><path stroke="#fff" stroke-linecap="round" d="M24.0083 18.0083V34"/></g></svg>'},{name:"vue",content:'<svg xmlns="http://www.w3.org/2000/svg" width="37.07" height="32" viewBox="0 0 256 221"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44
10h47.36Z"/><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"/><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"/></svg>'}];var a=s(49214);function c(e){const[n,s]=(0,r.useState)(""),[c,d]=(0,r.useState)(!1),u=e.className?e.className.replace(/language-/,""):"javascript",h=e.metastring?e.metastring.match(/title="([^"]+)"/):null,p=h?h[1]:null,m=e.metastring?e.metastring.match(/{([\d,-]+)}/):null,{colorMode:g}=(0,l.G)(),f={ts:t.find((e=>"ts"===e.name))?.content,js:t.find((e=>"js"===e.name))?.content,css:t.find((e=>"css"===e.name))?.content,json:t.find((e=>"json"===e.name))?.content,txt:t.find((e=>"text"===e.name))?.content,vue:t.find((e=>"vue"===e.name))?.content},x=(e,n)=>(0,a.jsx)("span",{className:n,dangerouslySetInnerHTML:{__html:e}});(0,r.useEffect)((()=>{(async()=>{let n=[];m&&(n=m[1].split(",").flatMap((e=>{const[n,s]=e.split("-").map(Number);return s?Array.from({length:s-n+1},((e,s)=>n+s)):[n]})));const r=await(0,o.Yz)(e.children,{lang:u,theme:"dark"===g?"vitesse-dark":"vitesse-light",transformers:[{name:"line-highlight",code(e){let s=0;return e.children.forEach((e=>{"element"===e.type&&"span"===e.tagName&&"line"===e.properties.class&&(s++,n.includes(s)&&(e.properties.class="line highlighted-line"))})),e}}]});s(r)})()}),[e.children,g,u,m]);return(0,a.jsx)(a.Fragment,{children:(0,a.jsxs)("div",{className:i["shiki-wrapper"],children:[(0,a.jsxs)("div",{className:i["code-header"],children:[p&&(0,a.jsxs)("div",{className:i["code-title"],children:[(e=>{if(!e)return null;const n=e.split(".").pop().toLowerCase(),s=f[n];return s?x(s,i["file-icon"]):null})(p),p]}),(0,a.jsx)("button",{className:i["copy-button"],onClick:()=>{navigator.clipboard.writeText(e.children).then((()=>{d(!0),setTimeout((()=>d(!1)),2e3)}))},children:c?(0,a.jsx)("svg",{className:`${i["copy-icon"]} ${i["copy-success"]}`,viewBox:"0 0 24 24",children:(0,a.jsx)("path",{d:"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z"})}):(0,a.jsx)("svg",{className:i["copy-icon"],viewBox:"0 0 24 24",children:(0,a.jsx)("path",{d:"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"})})})]}),(0,a.jsx)("div",{className:i["code-content"],dangerouslySetInnerHTML:{__html:n}})]})})}},71947:(e,n,s)=>{s.d(n,{A:()=>l});s(48318);var r=s(23372);const i={tabItem:"tabItem_Q1qS"};var o=s(49214);function l(e){let{children:n,hidden:s,className:l}=e;return(0,o.jsx)("div",{role:"tabpanel",className:(0,r.A)(i.tabItem,l),hidden:s,children:n})}},12790:(e,n,s)=>{s.d(n,{A:()=>w});var r=s(48318),i=s(23372),o=s(52521),l=s(66325),t=s(97478),a=s(37202),c=s(42587),d=s(7152);function u(e){return r.Children.toArray(e).filter((e=>"\n"!==e)).map((e=>{if(!e||(0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return e;throw new Error(`Docusaurus error: Bad <Tabs> child <${"string"==typeof e.type?e.type:e.type.name}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.`)}))?.filter(Boolean)??[]}function h(e){const{values:n,children:s}=e;return(0,r.useMemo)((()=>{const e=n??function(e){return u(e).map((e=>{let{props:{value:n,label:s,attributes:r,default:i}}=e;return{value:n,label:s,attributes:r,default:i}}))}(s);return function(e){const n=(0,c.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>e.value)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[n,s])}function p(e){let{value:n,tabValues:s}=e;return s.some((e=>e.value===n))}function m(e){let{queryString:n=!1,groupId:s}=e;const i=(0,l.W6)(),o=function(e){let{queryString:n=!1,groupId:s}=e;if("string"==typeof n)return n;if(!1===n)return null;if(!0===n&&!s)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return s??null}({queryString:n,groupId:s});return[(0,a.aZ)(o),(0,r.useCallback)((e=>{if(!o)return;const n=new URLSearchParams(i.location.search);n.set(o,e),i.replace({...i.location,search:n.toString()})}),[o,i])]}function g(e){const{defaultValue:n,queryString:s=!1,groupId:i}=e,o=h(e),[l,a]=(0,r.useState)((()=>function(e){let{defaultValue:n,tabValues:s}=e;if(0===s.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(n){if(!p({value:n,tabValues:s}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${n}" but none of its children has the corresponding value. Available values are: ${s.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return n}const r=s.find((e=>e.default))??s[0];if(!r)throw new Error("Unexpected error: 0 tabValues");return r.value}({defaultValue:n,tabValues:o}))),[c,u]=m({queryString:s,groupId:i}),[g,f]=function(e){let{groupId:n}=e;const s=function(e){return e?`docusaurus.tab.${e}`:null}(n),[i,o]=(0,d.Dv)(s);return[i,(0,r.useCallback)((e=>{s&&o.set(e)}),[s,o])]}({groupId:i}),x=(()=>{const e=c??g;return p({value:e,tabValues:o})?e:null})();(0,t.A)((()=>{x&&a(x)}),[x]);return{selectedValue:l,selectValue:(0,r.useCallback)((e=>{if(!p({value:e,tabValues:o}))throw new Error(`Can't select invalid tab value=${e}`);a(e),u(e),f(e)}),[u,f,o]),tabValues:o}}var f=s(3662);const x={tabList:"tabList_vzF7",tabItem:"tabItem_gx_e"};var j=s(49214);function y(e){let{className:n,block:s,selectedValue:r,selectValue:l,tabValues:t}=e;const a=[],{blockElementScrollPositionUntilNextRender:c}=(0,o.a_)(),d=e=>{const n=e.currentTarget,s=a.indexOf(n),i=t[s].value;i!==r&&(c(n),l(i))},u=e=>{let n=null;switch(e.key){case"Enter":d(e);break;case"ArrowRight":{const s=a.indexOf(e.currentTarget)+1;n=a[s]??a[0];break}case"ArrowLeft":{const s=a.indexOf(e.currentTarget)-1;n=a[s]??a[a.length-1];break}}n?.focus()};return(0,j.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,i.A)("tabs",{"tabs--block":s},n),children:t.map((e=>{let{value:n,label:s,attributes:o}=e;return(0,j.jsx)("li",{role:"tab",tabIndex:r===n?0:-1,"aria-selected":r===n,ref:e=>a.push(e),onKeyDown:u,onClick:d,...o,className:(0,i.A)("tabs__item",x.tabItem,o?.className,{"tabs__item--active":r===n}),children:s??n},n)}))})}function b(e){let{lazy:n,children:s,selectedValue:o}=e;
1const l=(Array.isArray(s)?s:[s]).filter(Boolean);if(n){const e=l.find((e=>e.props.value===o));return e?(0,r.cloneElement)(e,{className:(0,i.A)("margin-top--md",e.props.className)}):null}return(0,j.jsx)("div",{className:"margin-top--md",children:l.map(((e,n)=>(0,r.cloneElement)(e,{key:n,hidden:e.props.value!==o})))})}function v(e){const n=g(e);return(0,j.jsxs)("div",{className:(0,i.A)("tabs-container",x.tabList),children:[(0,j.jsx)(y,{...n,...e}),(0,j.jsx)(b,{...n,...e})]})}function w(e){const n=(0,f.A)();return(0,j.jsx)(v,{...e,children:u(e.children)},String(n))}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.