Plugma

repository·main·Indexed 41 days ago

https://github.com/gavinmcfarland/plugma

A command-line tool to simplify Figma plugin and widget development. It provides Hot Module Reloading (HMR), in-browser previews, unified bundling, and environment variable support. Includes the create-plugma CLI for scaffolding projects and supports various frameworks including React, Svelte, and Vue.

Tokens
36.8K
Snippets
169
Records
215
Agent score
100%

What's inside Plugma

  1. New features in Plugma V1

    main

    Plugma V1 introduces several improvements for Figma plugin development:

    • Improved Browser Previews: Messages now wait for the WebSocket connection to open before sending.
    • Theme Syncing: Figma's dark/light mode syncs instantly via an improved iframe setup.
    • Environment Management: Use the --mode flag and .env files to test different configurations without extra config files.
    • Unified Build System: All builds (development and production) are managed through a single vite.config.js file.
    • Developer Tools: Activate developer tools instantly using Cmd + Opt + J without requiring a restart.
    • Minimized Browser Preview: Preview in the browser with a minimized plugin window in Figma to save workspace space.
  2. Understand the previous Plugma architecture and task flows

    main
    This document outlines the legacy architecture of Plugma, detailing the core tasks, execution flows, and development environments used in previous versions. It describes how the system handled file watching, manifest building, UI/Main builds, and the integration of development 'apps' like ViteApp and PluginWindow to simulate the Figma environment.
  3. Core responsibilities of the Figma Bridge

    main

    The Figma Bridge provides several critical services for plugin developers:

    • Iframe Management: Maintains a persistent execution context for reliable communication between the main thread, iframe, browser, and testing clients.
    • Bi-directional Communication: Relays messages between Figma (parent window/main thread), the Iframe UI, Browser clients, and Test clients.
    • Figma Styles Sync: Monitors and synchronizes Figma's classes and styles so the plugin UI matches the Figma theme.
    • Developer Tools: Provides a development toolbar (activated via keyboard shortcut).
    • Server Status Monitoring: Monitors and displays the status of the development server to alert users if the server goes down.
  4. Implement the Task Definition pattern

    main

    When defining tasks, follow this standard pattern:

    1. Validate inputs: Check the provided opts and throw errors if requirements aren't met.
    2. Yield progress: Use yield with a logging mechanism to report status updates.
    3. Return results: Return the final computation or status object.
    task('name', function* (opts) {
      // 1. Validate inputs
      if (!opts.required) throw new Error('Missing required option')
    
      // 2. Yield progress/status
      yield log('Processing...')
    
      // 3. Return results
      return { success: true }
    })
  5. Understanding the Figma Bridge architecture and communication flow

    main

    The Figma Bridge acts as a wrapper and container for Figma plugins. It manages plugin communication, state, and development tools. Its primary purpose is to maintain a persistent execution context via iframe management, allowing reliable message relay between multiple clients.

    Communication Flow

    The Bridge facilitates bi-directional communication across four main entities:

    1. Figma main ↔ Figma Bridge ↔ Iframe UI (The plugin's actual UI)
    2. WebSocket Server ↔ Browser client
    3. WebSocket Server ↔ Test client
    1. Figma main ◄────► Figma Bridge ◄────► Iframe UI
    2. WebSocket Server ◄────► Browser client
    3. WebSocket Server ◄────► Test client
  6. How the Plugma Commands System works

    main

    The Plugma CLI is built on a task-based architecture. Instead of monolithic scripts, commands are composed of a sequence of reusable, self-contained Tasks.

    Core Concepts

    • Tasks: The smallest units of work. Each task has its own type definitions and returns a strongly typed result.
    • Commands: High-level operations (like dev or build) that orchestrate a specific sequence of tasks.
    • Task Results: The typed values returned by a task, which can be consumed by subsequent tasks in the sequence.
    • Task Context: A runtime object containing the command's configuration options and the results of all previously executed tasks.
    • Command Options: Configuration flags specific to a command (e.g., --port for the dev command).
  7. How the TaskRunner system works

    main

    The TaskRunner is a type-safe, dependency-aware system for managing task execution. It supports serial and parallel execution, built-in logging, performance tracking, and context sharing between tasks. Tasks are defined with a name, a run function that accepts options and context, and optional supportedCommands.

    type RegisteredTask<Name, Options, Results, Context> = {
        name: Name
        run: (options: Options, context: Context) => Promise<Results>
        supportedCommands?: string[]
    }
  8. Implement the WebSocket Communication pattern

    main

    WebSocket communication follows a three-step pattern:

    1. Create server: Initialize the server with a configuration object.
    2. Send message: Use server.send({ type, data }) to push updates.
    3. Handle response: Use server.on('message', callback) to listen for incoming messages and switch on the msg.type to process them.
    // 1. Create server
    // const server = await createServer(config)
    
    // 2. Send message
    // await server.send({ type: 'update', data: {} })
    
    // 3. Handle response
    // server.on('message', (msg) => {
    //   switch (msg.type) {
    //     case 'response':
    //       // Handle response
    //       break
    //   }
    // })
  9. Preserve component state during HMR

    main

    Hot Module Replacement (HMR) in Svelte can sometimes cause local component state to be lost. To prevent this, avoid storing critical state inside the component itself. Instead, use an external store that exists outside the component lifecycle.

    Implementation pattern

    Create a separate file for your store to ensure it is not replaced during HMR:

    // store.ts
    // An extremely simple external store
    import { writable } from "svelte/store";
    export default writable(0);
  10. Select a version bump type in Lerna

    main

    During the npx lerna publish process, you will be prompted to select a version bump type. Choose the one that matches your changes:

    • patch: For backwards-compatible bug fixes.
    • minor: For new backwards-compatible functionality.
    • major: For breaking changes.
    • custom: To specify a specific version manually.
  11. How shared code syncing works in Plugma

    main

    Plugma uses a "folded shared code" approach to manage dependencies. Instead of requiring @plugma/shared as a runtime dependency (which cannot be installed via npm), the code from @plugma/shared is copied directly into the src/shared/ directory of the plugma package.

    This ensures that the published package is self-contained and does not rely on workspace-only packages. The synchronization process moves code from /packages/shared/src/ to /packages/plugma/src/shared/ before the TypeScript compilation and publishing steps.