Basic Plugins CORE+

WildflowerJS plugins extend the framework using the same unified entity model as components, stores, and pool entities.

Plugins share one model with the rest of the framework. Plugins, components, stores, and pool entities all share the same foundational architecture. They're all reactive entities with state, computed properties, methods, and lifecycle hooks. The key difference is that plugins extend the framework itself, adding new capabilities like custom directives, lifecycle hooks, and services.

The Unified Entity Model

In WildflowerJS, plugins share the same reactive patterns as components and stores:

Component
wildflower.component('x', {
    state: { count: 0 },

    computed: {
        doubled() {
            return this.count * 2
        }
    },

    increment() {
        this.count++
    }
})
Store
wildflower.store('x', {
    state: { count: 0 },

    computed: {
        doubled() {
            return this.count * 2
        }
    },

    increment() {
        this.count++
    }
})
Plugin
wildflower.plugin({
    name: 'x',
    state: { count: 0 },

    computed: {
        doubled() {
            return this.count * 2
        }
    },

    increment() {
        this.count++
    }
})

The differences are:

  • Registration method: wildflower.component() vs store() vs plugin()
  • Access pattern: Plugins are accessed via wildflower.$pluginName
  • Scope: Plugins are global and can extend framework behavior
  • Capabilities: Plugins can register directives, hooks, and services
Once you understand component patterns (state, computed, methods, lifecycle), you already know how to write plugins. The same mental model applies throughout the framework.

Plugin Registration

Basic Plugin

Plugins are registered using wildflower.plugin(). The simplest form is a function:

// Function-based plugin
wildflower.plugin(function(wf, options) {
    // wf is the wildflower instance
    // options are passed during registration
    console.log('Plugin installed!')
})

// Object-based plugin with metadata
wildflower.plugin({
    name: 'my-plugin',
    version: '1.0.0',
    install(wf, options) {
        // Plugin installation logic
    }
})

Plugin Options

Pass configuration options when registering a plugin:

wildflower.plugin(myPlugin, {
    apiUrl: '/api',
    debug: true
})

Plugin State

Creating Plugins with State

Plugins can have their own reactive state, just like components and stores:

wildflower.plugin({
    name: 'notifications',
    version: '1.0.0',

    // Reactive state
    state: {
        items: [],
        unreadCount: 0
    },

    // Methods at top level (same as components/stores)
    add(message, type = 'info') {
        this.items.push({ message, type, id: Date.now() })
        this.unreadCount++
    },

    dismiss(id) {
        const index = this.items.findIndex(n => n.id === id)
        if (index !== -1) {
            this.items.splice(index, 1)
        }
    },

    clear() {
        this.items = []
        this.unreadCount = 0
    },

    // Computed properties
    computed: {
        hasUnread() {
            return this.unreadCount > 0
        }
    },

    install(wf) {
        // Optional installation logic
    }
})

Accessing Plugin State

Plugin state is accessible globally via $pluginName:

// Access state (shorthand: resolves through ContextProxy)
console.log(wildflower.$notifications.unreadCount)

// Call methods
wildflower.$notifications.add('New message!', 'success')
wildflower.$notifications.dismiss(123)

// Access computed (shorthand: no .computed. prefix needed)
if (wildflower.$notifications.hasUnread) {
    // Show badge
}

// Reset state to initial values
wildflower.$notifications.reset()

// Explicit access still works too:
// wildflower.$notifications.state.unreadCount
// wildflower.$notifications.computed.hasUnread

Custom Directives

Registering Directives

Create custom HTML attributes with wildflower.directive():

// Register a tooltip directive
wildflower.directive('tooltip', {
    init(element, value, context) {
        // Called when element enters DOM
        element.title = value
        element.classList.add('has-tooltip')
    },
    update(element, value, oldValue, context) {
        // Called when bound value changes
        element.title = value
    },
    destroy(element, value, context) {
        // Called when element leaves DOM
        element.classList.remove('has-tooltip')
    }
})

Using Directives

Use directives in HTML with data-{directive-name}:

<button data-tooltip="Click me!">Hover for tooltip</button>
<button data-tooltip="state.tooltipText">Dynamic tooltip</button>

Directive Context

The context parameter provides access to:

Property Description
context.component The parent component instance
context.path The bound state path
context.resolvedValue The current resolved value
context.listIndex Index when inside a list (or null)
context.listItem List item data when inside a list

Lifecycle Hooks

Available Hooks

Register callbacks for component lifecycle events:

// Hook into component initialization
wildflower.hook('component:beforeInit', (instance) => {
    console.log('Component starting:', instance.name)
})

wildflower.hook('component:afterInit', (instance) => {
    console.log('Component ready:', instance.name)
})

// Hook into component updates
wildflower.hook('component:beforeUpdate', (instance, changeInfo) => {
    console.log('About to update:', changeInfo.path, changeInfo.newValue)
})

wildflower.hook('component:afterUpdate', (instance, changeInfo) => {
    console.log('Updated:', changeInfo.path, 'from', changeInfo.oldValue, 'to', changeInfo.newValue)
})

// Hook into component destruction
wildflower.hook('component:beforeDestroy', (instance) => {
    // Cleanup before component is destroyed
})

wildflower.hook('component:afterDestroy', (instance) => {
    // After component is fully destroyed
})

Hook Use Cases

  • Logging/Analytics: Track component lifecycle for debugging or analytics
  • Performance Monitoring: Measure component initialization and update times
  • State Synchronization: Sync state changes with external systems
  • Cleanup: Ensure resources are properly released

Using Plugins in Components

Access plugins in computed properties for automatic dependency tracking:

wildflower.component('notification-bell', {
    computed: {
        // Plugin accessor is automatically reactive in computed properties!
        unreadCount() {
            const notifications = wildflower['$notifications']
            return notifications ? notifications.unreadCount : 0
        },
        hasUnread() {
            const notifications = wildflower['$notifications']
            return notifications ? notifications.hasUnread : false
        }
    },

    showNotification(message) {
        // Access the global plugin in methods
        wildflower.$notifications.add(message)
    },

    init() {
        // Read plugin state in lifecycle
        console.log('Unread:', wildflower.$notifications.unreadCount)
    }
})
Automatic Dependency Tracking: When wildflower['$pluginName'] is accessed inside a computed property, the framework automatically:
  • Detects which plugin properties are accessed
  • Registers the component as a dependent of that plugin
  • Re-evaluates the computed property when plugin state changes
No manual subscription needed!
Continue Learning: For advanced plugin patterns including service providers, watch/subscribe for state changes, DOM binding with $plugin.path, and complete plugin examples, see Advanced Plugin Patterns.