🔌 Плагины

В этом разделе мы рассмотрим систему плагинов StaticFlow и как создавать свои плагины.

🤔 Что такое плагин?

Плагин в StaticFlow - это расширение, которое добавляет новую функциональность или изменяет существующую. Плагины могут:

🏗️ Архитектура системы плагинов

Основные компоненты

Plugin (Базовый класс)

python
classPlugin(ABC):def__init__(self):self.config:Dict[str,Any]={}self.enabled:bool=Trueself._hooks:Dict[HookType,List[str]]={}

PluginMetadata (Метаданные)

python
@dataclassclassPluginMetadata:name:strversion:strdescription:strauthor:strdependencies:List[str]=Nonerequires_config:bool=Falsepriority:int=100

HookType (Типы хуков)

python
classHookType(Enum):PRE_BUILD=auto()# Перед сборкой сайтаPOST_BUILD=auto()# После сборки сайтаPRE_PAGE=auto()# Перед обработкой страницыPOST_PAGE=auto()# После обработки страницыPRE_TEMPLATE=auto()# Перед рендерингом шаблонаPOST_TEMPLATE=auto()# После рендеринга шаблонаPRE_ASSET=auto()# Перед обработкой ресурсаPOST_ASSET=auto()# После обработки ресурса

📦 Установка плагинов

Через pip

bash
pipinstallstaticflow-plugin-name

В конфигурации

toml
# config.toml[PLUGINS]enabled=["syntax_highlight","math","media","cdn","seo"][PLUGIN_SYNTAX_HIGHLIGHT]style="monokai"linenums=falsecss_class="highlight"[PLUGIN_MEDIA]output_dir="media"sizes={"thumbnail"={width=200,height=200}}formats=["webp","original"]

🛠️ Создание плагина

1. Структура плагина

scdoc
my-plugin/├── staticflow_my_plugin/│ ├── __init__.py│ ├── plugin.py│ └── utils.py├── tests/│ └── test_plugin.py├── setup.py├── README.md└── LICENSE

2. Основной код плагина

python
# staticflow_my_plugin/plugin.pyfromstaticflow.plugins.core.baseimportPlugin,PluginMetadatafromtypingimportDict,AnyclassMyPlugin(Plugin):@propertydefmetadata(self)->PluginMetadata:returnPluginMetadata(name="my-plugin",version="1.0.0",description="Описание моего плагина",author="Your Name",requires_config=False,priority=100)definitialize(self,config:Optional[Dict[str,Any]]=None)->None:"""Инициализация плагина."""super().initialize(config)# Ваша логика инициализацииdefon_pre_build(self,context:Dict[str,Any])->Dict[str,Any]:"""Хук перед сборкой сайта."""# Подготовка к сборкеreturncontextdefon_post_page(self,context:Dict[str,Any])->Dict[str,Any]:"""Хук после обработки страницы."""# Модификация контента страницыcontent=context.get('content','')# Ваша обработка контентаcontext['content']=contentreturncontextdefon_pre_asset(self,context:Dict[str,Any])->Dict[str,Any]:"""Хук перед обработкой ресурса."""# Обработка статических файловreturncontextdefcleanup(self)->None:"""Очистка ресурсов плагина."""# Очистка ресурсовpass

3. Регистрация плагина

python
# staticflow_my_plugin/__init__.pyfrom.pluginimportMyPlugindefregister(engine):plugin=MyPlugin()engine.add_plugin(plugin)returnplugin

4. Настройка setup.py

python
# setup.pyfromsetuptoolsimportsetup,find_packagessetup(name="staticflow-my-plugin",version="1.0.0",packages=find_packages(),install_requires=["staticflow>=1.0.0",],entry_points={"staticflow.plugins":["my-plugin = staticflow_my_plugin:register",],},author="Your Name",author_email="your.email@example.com",description="Описание моего плагина",long_description=open("README.md").read(),long_description_content_type="text/markdown",url="https://github.com/your-username/staticflow-my-plugin",classifiers=["Development Status :: 4 - Beta","Intended Audience :: Developers","License :: OSI Approved :: MIT License","Programming Language :: Python :: 3",],)

🎯 Типы плагинов

📝 Плагины контента

python
classContentPlugin(Plugin):defon_post_page(self,context:Dict[str,Any])->Dict[str,Any]:"""Обработка контента страницы."""content=context.get('content','')# Модификация контентаcontext['content']=processed_contentreturncontext

🎨 Плагины шаблонов

python
classTemplatePlugin(Plugin):defon_post_template(self,context:Dict[str,Any])->Dict[str,Any]:"""Обработка после рендеринга шаблона."""# Модификация контекста или шаблонаreturncontext

🖥️ Плагины CLI

python
classCLIPlugin(Plugin):defregister_commands(self,cli):@cli.command()defmy_command():"""Описание команды"""# Код командыpass

⚡ Плагины сборки

python
classBuildPlugin(Plugin):defon_pre_build(self,context:Dict[str,Any])->Dict[str,Any]:"""Подготовка к сборке."""# Подготовка ресурсовreturncontextdefon_post_build(self,context:Dict[str,Any])->Dict[str,Any]:"""Очистка после сборки."""# Очистка временных файловreturncontext

🔗 Хуки плагинов

🔄 Хуки жизненного цикла

📄 Хуки страниц

🎨 Хуки шаблонов

📁 Хуки ресурсов

⚙️ Конфигурация плагина

Настройки по умолчанию

python
classMyPlugin(Plugin):definitialize(self,config:Optional[Dict[str,Any]]=None)->None:default_config={'option1':'default1','option2':'default2',}# Объединение с пользовательской конфигурациейself.config={**default_config,**(configor{})}super().initialize()

Валидация настроек

python
defvalidate_config(self)->bool:"""Проверка конфигурации плагина."""required=['option1','option2']foroptioninrequired:ifoptionnotinself.config:raiseValueError(f"Missing required setting: {option}")returnTrue

🧪 Тестирование плагинов

Модульные тесты

python
# tests/test_plugin.pyimportpytestfromstaticflow_my_pluginimportMyPlugindeftest_plugin_initialization():plugin=MyPlugin()assertplugin.metadata.name=="my-plugin"assertplugin.metadata.version=="1.0.0"deftest_plugin_config():config={'option1':'value1'}plugin=MyPlugin()plugin.initialize(config)assertplugin.config['option1']=='value1'deftest_plugin_hooks():plugin=MyPlugin()context={'content':'test content'}# Тестирование хукаresult=plugin.on_post_page(context)assert'content'inresult

Интеграционные тесты

python
deftest_plugin_integration():fromstaticflow.core.engineimportEnginefromstaticflow.core.configimportConfig# Создание тестового движкаconfig=Config("test_config.toml")engine=Engine(config)# Добавление плагинаplugin=MyPlugin()engine.add_plugin(plugin)# Тестирование функциональностиassertengine.get_plugin("my-plugin")==plugin

📦 Встроенные плагины

🔍 SEO Plugin

python
# Автоматическое добавление Open Graph тегов# Twitter Card тегов# Schema.org разметки# Оптимизация заголовков и изображений

🗺️ Sitemap Plugin

python
# Генерация sitemap.xml# Автоматическое обновление# Поддержка приоритетов

📡 RSS Plugin

python
# Генерация RSS лент# Поддержка категорий# Автоматическое обновление

🖼️ Media Plugin

python
# Обработка изображений# Генерация WebP# Создание srcset# Оптимизация видео

🌐 CDN Plugin

python
# Интеграция с CDN# Автоматическая загрузка файлов# Очистка кэша

💎 Syntax Highlight Plugin

python
# Подсветка синтаксиса кода# Поддержка множества языков# Кастомизируемые темы

➗ Math Plugin

python
# Рендеринг математических формул# Поддержка LaTeX# Автоматическое рендеринг

📊 Mermaid Plugin

python
# Создание диаграмм# Поддержка различных типов# Интерактивные диаграммы

🚀 Публикация плагина

Подготовка

  1. 📝 Создайте README.md
  2. 📄 Добавьте лицензию
  3. 📚 Напишите документацию
  4. 🧪 Создайте тесты
  5. 🔧 Настройте CI/CD

Публикация в PyPI

bash
# Сборка пакетаpythonsetup.pysdistbdist_wheel# Загрузка в PyPItwineuploaddist/*

Обновление

  1. 🔢 Увеличьте версию
  2. 📚 Обновите документацию
  3. 🔄 Добавьте миграции
  4. 🚀 Опубликуйте новую версию

💡 Лучшие практики

🛠️ Разработка

  1. 📏 Следуйте PEP 8
  2. 🧪 Пишите тесты
  3. 📝 Документируйте код
  4. 🏷️ Используйте типизацию
  5. 🔍 Обрабатывайте ошибки

⚡ Производительность

  1. 🚀 Оптимизируйте операции
  2. 💾 Используйте кэширование
  3. 📦 Минимизируйте зависимости
  4. 💻 Следите за памятью
  5. 🔄 Используйте асинхронность

🔒 Безопасность

  1. ✅ Валидируйте входные данные
  2. 🔐 Используйте безопасные настройки
  3. ⚠️ Обрабатывайте ошибки
  4. 🔄 Следите за обновлениями
  5. 🛡️ Проверяйте зависимости

🔗 Совместимость

  1. 🐍 Поддерживайте версии Python
  2. 🧪 Тестируйте на разных версиях
  3. 📋 Документируйте зависимости
  4. 👀 Следите за изменениями API
  5. 🔄 Обеспечивайте обратную совместимость

🎯 Примеры использования

Простой плагин для добавления мета-тегов

python
classMetaTagsPlugin(Plugin):@propertydefmetadata(self)->PluginMetadata:returnPluginMetadata(name="meta-tags",version="1.0.0",description="Добавляет мета-теги к страницам",author="Your Name")defon_post_page(self,context:Dict[str,Any])->Dict[str,Any]:content=context.get('content','')# Добавляем мета-тегиmeta_tags=f""" <meta name="generator" content="StaticFlow"> <meta name="author" content="{context.get('author','Unknown')}"> <meta name="date" content="{context.get('date','')}"> """# Вставляем в headif'<head>'incontent:content=content.replace('<head>',f'<head>{meta_tags}')context['content']=contentreturncontext

Плагин для обработки изображений

python
classImageProcessorPlugin(Plugin):defon_pre_asset(self,context:Dict[str,Any])->Dict[str,Any]:file_path=context.get('file_path')ifself._is_image(file_path):# Обработка изображенияprocessed_path=self._process_image(file_path)context['file_path']=processed_pathreturncontextdef_is_image(self,path:str)->bool:returnpath.lower().endswith(('.png','.jpg','.jpeg','.gif'))def_process_image(self,path:str)->str:# Логика обработки изображенияreturnpath