/* @flow */ import warning from 'tiny-warning' import type StyleSheet from './StyleSheet' import type { Plugin, Rule, RuleOptions, UpdateOptions, JssStyle, JssValue, OnCreateRule, OnProcessRule, OnProcessStyle, OnProcessSheet, OnChangeValue, OnUpdate } from './types' import type {StyleRule} from './plugins/styleRule' type Registry = { onCreateRule: Array<OnCreateRule>, onProcessRule: Array<OnProcessRule>, onProcessStyle: Array<OnProcessStyle>, onProcessSheet: Array<OnProcessSheet>, onChangeValue: Array<OnChangeValue>, onUpdate: Array<OnUpdate> } export default class PluginsRegistry { plugins: { internal: Array<Plugin>, external: Array<Plugin> } = { internal: [], external: [] } registry: Registry /** * Call `onCreateRule` hooks and return an object if returned by a hook. */ onCreateRule(name?: string, decl: JssStyle, options: RuleOptions): Rule | null { for (let i = 0; i < this.registry.onCreateRule.length; i++) { const rule = this.registry.onCreateRule[i](name, decl, options) if (rule) return rule } return null } /** * Call `onProcessRule` hooks. */ onProcessRule(rule: Rule): void { if (rule.isProcessed) return const {sheet} = rule.options for (let i = 0; i < this.registry.onProcessRule.length; i++) { this.registry.onProcessRule[i](rule, sheet) } if (rule.style) this.onProcessStyle(rule.style, rule, sheet) rule.isProcessed = true } /** * Call `onProcessStyle` hooks. */ onProcessStyle(style: JssStyle, rule: Rule, sheet?: StyleSheet): void { for (let i = 0; i < this.registry.onProcessStyle.length; i++) { // $FlowFixMe rule.style = this.registry.onProcessStyle[i](rule.style, rule, sheet) } } /** * Call `onProcessSheet` hooks. */ onProcessSheet(sheet: StyleSheet): void { for (let i = 0; i < this.registry.onProcessSheet.length; i++) { this.registry.onProcessSheet[i](sheet) } } /** * Call `onUpdate` hooks. */ onUpdate(data: Object, rule: Rule, sheet: StyleSheet, options: UpdateOptions): void { for (let i = 0; i < this.registry.onUpdate.length; i++) { this.registry.onUpdate[i](data, rule, sheet, options) } } /** * Call `onChangeValue` hooks. */ onChangeValue(value: JssValue, prop: string, rule: StyleRule): JssValue { let processedValue = value for (let i = 0; i < this.registry.onChangeValue.length; i++) { processedValue = this.registry.onChangeValue[i](processedValue, prop, rule) } return processedValue } /** * Register a plugin. */ use(newPlugin: Plugin, options: {queue: 'internal' | 'external'} = {queue: 'external'}): void { const plugins = this.plugins[options.queue] // Avoids applying same plugin twice, at least based on ref. if (plugins.indexOf(newPlugin) !== -1) { return } plugins.push(newPlugin) this.registry = [...this.plugins.external, ...this.plugins.internal].reduce( (registry: Registry, plugin: Plugin) => { for (const name in plugin) { if (name in registry) { registry[name].push(plugin[name]) } else { warning(false, `[JSS] Unknown hook "${name}".`) } } return registry }, { onCreateRule: [], onProcessRule: [], onProcessStyle: [], onProcessSheet: [], onChangeValue: [], onUpdate: [] } ) } }