# Unplugin Export Plugin

`@talex-touch/unplugin-export-plugin` is a build tool plugin for Tuff plugin development, handling automatic resource export, manifest generation, and dev server integration.

For regular plugin projects, prefer `@talex-touch/tuff-cli` for creation, build, and publish workflows. `unplugin-export-plugin` is the lower-level build layer reused by the CLI and is only recommended when you need a custom Vite / Webpack / Rollup integration.

## Installation

:::TuffCodeBlock{lang="bash"}
---
code: |
  pnpm add -D @talex-touch/unplugin-export-plugin
---
:::

## Quick Start

**Vite Configuration**

:::TuffCodeBlock{lang="ts"}
---
code: |
  // vite.config.ts
  import TouchPluginExport from '@talex-touch/unplugin-export-plugin/vite'
  import vue from '@vitejs/plugin-vue'
  import UnoCSS from 'unocss/vite'
  import { defineConfig } from 'vite'

  export default defineConfig({
    plugins: [
      vue(),
      UnoCSS(),
      TouchPluginExport()
    ],
    server: {
      port: 6001  // Dev server port
    }
  })
---
:::

**Webpack Configuration**

:::TuffCodeBlock{lang="js"}
---
code: |
  // webpack.config.js
  const TouchPluginExport = require('@talex-touch/unplugin-export-plugin/webpack')

  module.exports = {
    plugins: [
      TouchPluginExport()
    ]
  }
---
:::

**Rollup Configuration**

:::TuffCodeBlock{lang="js"}
---
code: |
  // rollup.config.js
  import TouchPluginExport from '@talex-touch/unplugin-export-plugin/rollup'

  export default {
    plugins: [
      TouchPluginExport()
    ]
  }
---
:::

---

## Features

**1. Automatic Manifest Processing**

The plugin automatically reads `manifest.json` from project root and during build:
- Validates required fields
- Injects version information
- Generates production manifest

**2. Resource Export**

Automatically handles:
- **HTML entry files**: Auto-injects necessary scripts and styles
- **Static assets**: Copies `public/` directory to output
- **Icon files**: Processes icons declared in manifest

**Generated-output isolation**

The exporter reserves `<outDir>/out`, `<outDir>/build`, and top-level `<outDir>/*.tpex` as generated output. Repeated builds never restage those entries as source payload, preventing prior archives from being nested in the next package or recorded in `manifest._files`.

**3. Development Mode Support**

In development mode:
- Automatic HMR (Hot Module Replacement) configuration
- Generates development manifest
- Seamless integration with Tuff main process

---

## Configuration Options

:::TuffCodeBlock{lang="ts"}
---
code: |
  import { defineConfig } from '@talex-touch/unplugin-export-plugin/types'

  // Use defineConfig for type hints
  export default defineConfig({
    // Project root directory
    root: process.cwd(),

    // manifest.json path (relative to project root)
    manifest: './manifest.json',

    // Output directory
    outDir: 'dist',

    // Source directory (for resource loading)
    sourceDir: 'src',

    // Widgets directory
    widgetsDir: 'widgets',

    // Public assets directory
    publicDir: 'public',

    // Index script directory (Prelude script)
    indexDir: 'index',

    // Generate source maps
    sourcemap: false,

    // Minify output
    minify: true,

    // External dependencies (not bundled)
    external: ['electron'],

    // Custom asset handling
    assets: {
      // Additional files/directories to copy (glob patterns)
      copy: ['./assets/**/*', './public/icons/*'],

      // Excluded files (glob patterns)
      exclude: ['**/*.test.ts', '**/*.spec.js']
    },

    // Version sync options
    versionSync: {
      // Enable syncing version from package.json to manifest.json
      enabled: true,

      // Auto sync without prompting
      auto: false
    },

    // Maximum plugin size (MB) before warning
    maxSizeMB: 10
  })
---
:::

**Configuration Reference**

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `root` | `string` | `process.cwd()` | Project root directory |
| `manifest` | `string` | `'./manifest.json'` | manifest.json path |
| `outDir` | `string` | `'dist'` | Output directory |
| `sourceDir` | `string` | `'src'` | Source directory |
| `widgetsDir` | `string` | `'widgets'` | Widgets directory |
| `publicDir` | `string` | `'public'` | Public assets directory |
| `indexDir` | `string` | `'index'` | Prelude script directory |
| `sourcemap` | `boolean` | `false` | Generate source maps |
| `minify` | `boolean` | `true` | Minify output |
| `external` | `string[]` | `['electron']` | External dependencies |
| `assets.copy` | `string[]` | `undefined` | Additional files to copy |
| `assets.exclude` | `string[]` | `undefined` | Files to exclude |
| `versionSync.enabled` | `boolean` | `false` | Enable version sync |
| `versionSync.auto` | `boolean` | `false` | Auto sync version |
| `maxSizeMB` | `number` | `10` | Max plugin size warning threshold |

**Version Sync**

When `versionSync.enabled` is `true`, the build process checks if `package.json` and `manifest.json` versions match:

- Prompts user to sync when versions differ
- Set `versionSync.auto: true` to auto-sync without confirmation
- Can also trigger via `--sync-version` CLI flag

**Size Warning**

When the packaged `.tpex` file exceeds `maxSizeMB` (default 10MB), a warning is shown:

:::TuffCodeBlock{lang="vue"}
---
code: |
  ⚠ WARNING  Plugin size (12.34 MB) exceeds 10 MB limit!
    → Consider optimizing assets or splitting into smaller plugins
    → Large plugins may be rejected by the plugin store
---
:::

---

## Project Structure

Recommended plugin project structure:

:::TuffCodeBlock{lang="vue"}
---
code: |
  my-plugin/
  ├── public/
  │   └── icon.png           # Plugin icon
  ├── src/
  │   ├── main.ts            # Entry file
  │   ├── App.vue            # Main component
  │   └── components/        # Components directory
  ├── index.html             # HTML entry
  ├── manifest.json          # Plugin manifest
  ├── vite.config.ts         # Vite config
  ├── package.json
  └── tsconfig.json
---
:::

---

## Manifest Example

:::TuffCodeBlock{lang="json"}
---
code: |
  {
    "id": "com.example.my-plugin",
    "name": "My Plugin",
    "version": "1.0.0",
    "description": "A sample Tuff plugin",
    "author": "Your Name",
    "icon": "public/icon.png",
    "main": "index.html",
    "features": [
      {
        "id": "search",
        "name": "Search Feature",
        "description": "Quick search",
        "keywords": ["search", "find"],
        "icon": "ri:search-line"
      }
    ],
    "dev": {
      "enable": true,
      "port": 6001
    }
  }
---
:::

---

## Development Mode

**Enabling Development Mode**

Configure in `manifest.json`:

:::TuffCodeBlock{lang="json"}
---
code: |
  {
    "dev": {
      "enable": true,
      "port": 6001
    }
  }
---
:::

**Hot Reload**

In development mode, these changes trigger hot reload:
- Vue component changes
- CSS/SCSS changes
- JavaScript/TypeScript changes

`manifest.json` changes trigger plugin reload.

**Debugging Tips**

1. **Open DevTools**: Press `Cmd+Option+I` (Mac) or `Ctrl+Shift+I` (Windows) in plugin window
2. **View logs**: Main process logs output to terminal
3. **Breakpoint debugging**: Use Chrome DevTools to set breakpoints

---

## Building for Production

:::TuffCodeBlock{lang="bash"}
---
code: |
  pnpm build
---
:::

Build output:

:::TuffCodeBlock{lang="vue"}
---
code: |
  dist/
  ├── assets/
  │   ├── index-[hash].js
  │   └── index-[hash].css
  ├── index.html
  ├── manifest.json
  └── icon.png
---
:::

---

## SDK Integration

After building, use the SDK from `@talex-touch/utils`:

:::TuffCodeBlock{lang="ts"}
---
code: |
  // src/main.ts
  import { useBox, useClipboard, usePluginStorage } from '@talex-touch/utils/plugin/sdk'

  const box = useBox()
  const clipboard = useClipboard()
  const storage = usePluginStorage()

  // Plugin logic...
---
:::

---

## FAQ

**Q: Plugin won't load after build**

1. Check if `manifest.json` `id` field follows format `com.xxx.xxx`
2. Ensure `main` field points to correct entry file
3. Check main process logs for detailed error messages

**Q: Styles not working in dev mode**

Ensure CSS preprocessor is configured correctly in `vite.config.ts`:

:::TuffCodeBlock{lang="ts"}
---
code: |
  export default defineConfig({
    css: {
      preprocessorOptions: {
        scss: {
          // SCSS config
        }
      }
    }
  })
---
:::

**Q: Resource path errors**

Use relative paths or `@/` alias:

:::TuffCodeBlock{lang="ts"}
---
code: |
  // vite.config.ts
  import { resolve } from 'path'

  export default defineConfig({
    resolve: {
      alias: {
        '@': resolve(__dirname, 'src')
      }
    }
  })
---
:::

---

## Example Projects

Check official example plugins:

- [touch-image](https://github.com/talex-touch/tuff/tree/main/plugins/touch-image) - Image processing plugin
- [touch-translation](https://github.com/talex-touch/tuff/tree/main/plugins/touch-translation) - Translation plugin
- [touch-music](https://github.com/talex-touch/tuff/tree/main/plugins/touch-music) - Music player plugin

## Changelog

See [CHANGELOG.md](https://github.com/talex-touch/tuff/blob/main/packages/unplugin-export-plugin/CHANGELOG.md) for release notes.
