# Clash Nyanpasu Profile Enhancement System: JavaScript and Lua Scripting Guide

> Master Clash Nyanpasu's profile enhancement system with JavaScript and Lua scripting. Transform Clash configurations dynamically before processing. Learn how now.

- Repository: [Nyanpasu/clash-nyanpasu](https://github.com/libnyanpasu/clash-nyanpasu)
- Tags: deep-dive
- Published: 2026-03-06

---

**Clash Nyanpasu’s profile enhancement system allows users to transform Clash configurations on-the-fly using JavaScript or Lua scripts that execute before the core consumes the YAML.**

The profile enhancement system in Clash Nyanpasu bridges the gap between static configuration files and dynamic runtime needs. By leveraging embedded JavaScript and Lua interpreters, users can programmatically modify, augment, or sanitize their Clash configs without leaving the GUI. This article examines the architecture, implementation, and practical usage of the profile enhancement system based on the `libnyanpasu/clash-nyanpasu` source code.

## How the Profile Enhancement System Works

The system operates through three distinct layers: data model definitions, an execution engine that chains scripts together, and a user interface for managing enhancements.

### Profile Data Model and Script Types

At the storage layer, a profile is represented as a YAML node. When the profile `type` is `script`, the node must include a `script_type` field specifying either `javascript` or `lua`.

In [`backend/tauri/src/config/profile/item/shared.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/profile/item/shared.rs), the Rust enum `ProfileItemType::Script(ScriptType)` handles serialization. The `ScriptType` enum (defined in [`backend/tauri/src/enhance/chain.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs) at lines 34-41) explicitly distinguishes between the two languages:

```rust
pub enum ScriptType {
    JavaScript,
    Lua,
}

```

### Built-in Enhancement Chain

Clash Nyanpasu ships with several built-in scripts—such as [`meta_guard.js`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/meta_guard.js), [`config_fixer.js`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/config_fixer.js), and [`clash_rs_comp.lua`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/clash_rs_comp.lua)—that ensure compatibility across different Clash cores.

The `ChainItem::builtin()` function in [`backend/tauri/src/enhance/chain.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs) (lines 45-75) constructs a vector of `(BitFlags<ClashCore>, ChainItem)` pairs. Each item wraps a script using either `ChainTypeWrapper::new_js` or `ChainTypeWrapper::new_lua`, depending on the file extension. When a profile loads, the runtime selects the appropriate chain based on the active core (Mihomo, Clash Rs, etc.) and executes scripts sequentially, mutating the configuration object in memory.

### User-Defined Scripts

Users can inject custom logic through the frontend interface. The **Import Chain Profile** modal (`frontend/nyanpasu/src/pages/(main)/main/profiles/$type/_modules/chain-profile-import.tsx`) presents a form schema that includes a `script_type` field validated as `z.literal('javascript').or(z.literal('lua')).nullable()`.

Upon submission, the profile is stored via `useProfile().create`. Editing an existing script opens [`script-dialog.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/script-dialog.tsx), which renders a Monaco-based code editor. The dialog switches between `ProfileTemplate.javascript` and `ProfileTemplate.luascript` templates (defined in [`frontend/interface/src/template/index.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/template/index.ts)) based on the profile's `script_type`.

### Runtime Execution

When Clash starts, the frontend transmits the full profile list to the backend through the Tauri bridge. The backend extracts all items where `type == "script"`, converts each into a `ChainItem` via `ChainItem::to_script(uid, …)`, and appends them to the built-in chain.

JavaScript snippets execute under **QuickJS** (via the `javascriptcore-rs` crate), while Lua snippets run under **rlua** (exposed as `ChainTypeWrapper::new_lua`). The combined chain transforms the raw YAML before the Clash core ever reads it.

## Practical Usage Examples

### Adding a JavaScript Enhancement via the UI

Navigate to the Profiles page and click the **Import Chain Profile** button. In the modal:

1. Set **Script Type** to `JavaScript`.
2. Enter a name and optional description.
3. Submit the form.

The system stores the profile as:

```json
{
  "uid": "auto-generated-uuid",
  "type": "script",
  "script_type": "javascript",
  "name": "My JS Enhancer",
  "file": null,
  "desc": "Custom DNS routing logic"
}

```

Editing the profile opens the Monaco editor pre-filled with the JavaScript template.

### Defining a Lua Script Manually

You can also author scripts directly in YAML files. Create `~/.config/clash/nyanpasu/profiles/strip-fields.yaml`:

```yaml
type: script
script_type: lua
name: "Strip unsupported fields"
file: |
  -- Lua code executed before the core reads the config
  function enhance(config)
    config["port"] = nil   -- remove port key for Clash Rs compatibility
    return config
  end

```

When selected, the backend loads `script_type = lua`, wraps it with `ChainTypeWrapper::new_lua`, and injects it into the execution chain.

### Using the Built-in Chain Programmatically

For developers extending the backend, you can construct script items manually in Rust:

```rust
use crate::enhance::{ChainItem, ChainTypeWrapper};

let custom_js = ChainItem::to_script(
    "my_custom_js",
    ChainTypeWrapper::new_js(
        "function enhance(cfg) { cfg.dns = { enable: true }; return cfg; }".to_string()
    ),
);

let mut full_chain = ChainItem::builtin();
full_chain.push((ClashCore::Mihomo, custom_js));

```

This appends your JavaScript enhancer to the default built-in chain before the core launches.

## Key Source Files

| Purpose | File | Link |
|---------|------|------|
| Profile data model (`script_type`) | [`backend/tauri/src/config/profile/item/shared.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/profile/item/shared.rs) | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/profile/item/shared.rs |
| Script language enum (`ScriptType`) | [`backend/tauri/src/enhance/chain.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs) (lines 34-41) | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs#L34-L41 |
| Built-in enhancement chain | [`backend/tauri/src/enhance/chain.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs) (lines 45-75) | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs#L45-L75 |
| UI for importing script profiles | `frontend/nyanpasu/src/pages/(main)/main/profiles/$type/_modules/chain-profile-import.tsx` | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/nyanpasu/src/pages/(main)/main/profiles/$type/_modules/chain-profile-import.tsx |
| Monaco-based script editor | [`frontend/nyanpasu/src/components/profiles/script-dialog.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/nyanpasu/src/components/profiles/script-dialog.tsx) | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/nyanpasu/src/components/profiles/script-dialog.tsx |
| Profile type constants | `frontend/nyanpasu/src/pages/(main)/main/profiles/_modules/consts.ts` | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/nyanpasu/src/pages/(main)/main/profiles/_modules/consts.ts |
| Script templates (JS/Lua) | [`frontend/interface/src/template/index.ts`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/template/index.ts) | https://github.com/libnyanpasu/clash-nyanpasu/blob/main/frontend/interface/src/template/index.ts |

## Summary

- **Clash Nyanpasu’s profile enhancement system** enables dynamic configuration transformation through JavaScript and Lua scripts executed before the core reads the YAML.
- **Script types** are defined in the data model (`ProfileItemType::Script`) with explicit `script_type` fields distinguishing JavaScript from Lua.
- **Execution engine** chains built-in scripts ([`meta_guard.js`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/meta_guard.js), [`clash_rs_comp.lua`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/clash_rs_comp.lua)) with user-defined snippets using `ChainItem::builtin()` and `ChainTypeWrapper` for QuickJS and rlua runtimes.
- **Frontend interface** provides Monaco-based editing through [`script-dialog.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/script-dialog.tsx) and import workflows via [`chain-profile-import.tsx`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/chain-profile-import.tsx), supporting both languages transparently.

## Frequently Asked Questions

### What languages does the profile enhancement system support?

The system supports **JavaScript** and **Lua**. These are defined in the `ScriptType` enum in [`backend/tauri/src/enhance/chain.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/enhance/chain.rs), and the frontend enforces these values through Zod schema validation in the import modal.

### How are JavaScript and Lua scripts executed in Clash Nyanpasu?

JavaScript snippets run under **QuickJS** via the `javascriptcore-rs` crate, while Lua snippets execute under **rlua**. The backend wraps each script in a `ChainTypeWrapper` (`new_js` or `new_lua`) and processes them sequentially through the enhancement chain before passing the final YAML to the Clash core.

### Can I combine built-in and custom scripts in the same profile?

Yes. The system appends user-defined scripts to the built-in chain at runtime. When you create a script profile through the UI or manually define one in YAML, the backend calls `ChainItem::to_script()` and pushes it into the vector returned by `ChainItem::builtin()`, ensuring both built-in fixes and your custom logic execute in sequence.

### Where are script-based profiles stored and managed?

Script profiles are stored as YAML files in the Nyanpasu configuration directory (typically `~/.config/clash/nyanpasu/profiles/`). Each file contains the `type: script` and `script_type` fields. The frontend manages these through React hooks like `useProfile().create`, while the backend deserializes them via `ProfileItemType::Script` in [`backend/tauri/src/config/profile/item/shared.rs`](https://github.com/libnyanpasu/clash-nyanpasu/blob/main/backend/tauri/src/config/profile/item/shared.rs).