Plugins y Extensiones
El sistema de plugins de CWF te permite extender el formateador con reglas y funcionalidad personalizadas. Esta arquitectura modular facilita la creación, compartición y reutilización de reglas de formato entre proyectos.
Entendiendo los Plugins
Un plugin es un conjunto de reglas de formato que se pueden cargar en CWF. El sistema de plugins sigue el Principio Abierto/Cerrado - el formateador está abierto para extensión pero cerrado para modificación.
Plugin Core
CWF incluye un CorePlugin integrado que proporciona todas las reglas de formato estándar:
indentation- Normaliza espacios/tabuladoresline-ending- Normaliza finales de línea (LF/CRLF/CR)trailing-whitespace- Elimina espacios al finalfinal-newline- Asegura nueva línea finalmax-line-length- Valida/envuelve líneas largas
El plugin core se carga automáticamente cuando usas createFormatter().
Creando Plugins Personalizados
Estructura Básica de Plugin
import { BasePlugin, IFormattingRule } from '@codewaveinnovation/formatter';
export class MiPlugin extends BasePlugin {
readonly name = 'mi-plugin';
readonly version = '1.0.0';
getRules(): IFormattingRule[] {
return [
new MiReglaPersonalizada(),
new OtraReglaPersonalizada(),
];
}
}Creando una Regla Personalizada
Las reglas deben extender BaseFormattingRule:
import { BaseFormattingRule, FormatContext } from '@codewaveinnovation/formatter';
export class ReglaPuntoYComa extends BaseFormattingRule {
readonly name = 'semicolon';
readonly description = 'Aplica uso de punto y coma al final de declaraciones';
protected format(context: FormatContext): string {
const { content, config } = context;
// Obtener configuración de la regla
const ruleConfig = config.rules.find(r => r.name === this.name);
const options = ruleConfig?.options || { enforce: true };
if (options.enforce) {
// Agregar punto y coma donde falte
return this.addSemicolons(content);
} else {
// Eliminar punto y coma
return this.removeSemicolons(content);
}
}
private addSemicolons(content: string): string {
// Detalles de implementación...
return content;
}
private removeSemicolons(content: string): string {
// Detalles de implementación...
return content;
}
}Ejemplos de Plugins
Ejemplo 1: Plugin de Estilo de Comillas
import { BasePlugin, BaseFormattingRule, FormatContext } from '@codewaveinnovation/formatter';
class ReglaEstiloComillas extends BaseFormattingRule {
readonly name = 'quote-style';
readonly description = 'Normaliza estilo de comillas (simples/dobles)';
protected format(context: FormatContext): string {
const ruleConfig = context.config.rules.find(r => r.name === this.name);
const style = ruleConfig?.options?.style || 'single';
if (style === 'single') {
// Convertir comillas dobles a simples
return context.content.replace(/"([^"]*)"/g, "'$1'");
} else {
// Convertir comillas simples a dobles
return context.content.replace(/'([^']*)'/g, '"$1"');
}
}
}
export class PluginFormatoStrings extends BasePlugin {
readonly name = 'string-formatting';
readonly version = '1.0.0';
getRules(): IFormattingRule[] {
return [new ReglaEstiloComillas()];
}
}Uso:
import { createFormatter, RuleRegistry, PluginManager } from '@codewaveinnovation/formatter';
import { PluginFormatoStrings } from './PluginFormatoStrings';
const registry = new RuleRegistry();
const pluginManager = new PluginManager(registry);
// Cargar plugin personalizado
pluginManager.loadPlugin(new PluginFormatoStrings());
const formatter = new CodeFormatter(registry);
const config = {
rules: [
{ name: 'quote-style', enabled: true, options: { style: 'single' } }
]
};
const result = await formatter.format(code, config);Ejemplo 2: Plugin Multi-Regla
import { BasePlugin } from '@codewaveinnovation/formatter';
class ReglaFormatoComentario extends BaseFormattingRule {
readonly name = 'comment-format';
readonly description = 'Formatea comentarios consistentemente';
protected format(context: FormatContext): string {
// Asegurar espacio después de // en comentarios de línea única
return context.content.replace(/\/\/([^\s])/g, '// $1');
}
}
class ReglaEspaciadoCorchetes extends BaseFormattingRule {
readonly name = 'bracket-spacing';
readonly description = 'Controla espaciado dentro de corchetes';
protected format(context: FormatContext): string {
const ruleConfig = context.config.rules.find(r => r.name === this.name);
const spacing = ruleConfig?.options?.spacing !== false;
if (spacing) {
// Agregar espacio dentro de corchetes: { key: value }
return context.content
.replace(/\{([^\s{])/g, '{ $1')
.replace(/([^\s}])\}/g, '$1 }');
} else {
// Eliminar espacio dentro de corchetes: {key: value}
return context.content
.replace(/\{\s+/g, '{')
.replace(/\s+\}/g, '}');
}
}
}
export class PluginGuiaEstilo extends BasePlugin {
readonly name = 'style-guide';
readonly version = '1.0.0';
getRules(): IFormattingRule[] {
return [
new ReglaFormatoComentario(),
new ReglaEspaciadoCorchetes(),
];
}
}Cargando Plugins
Método 1: Carga Programática
import { RuleRegistry, PluginManager, CodeFormatter } from '@codewaveinnovation/formatter';
import { MiPlugin } from './MiPlugin';
const registry = new RuleRegistry();
const pluginManager = new PluginManager(registry);
// Cargar plugins
pluginManager.loadPlugin(new MiPlugin());
// Crear formateador
const formatter = new CodeFormatter(registry);Método 2: Usando createFormatter con Registry Personalizado
import { createFormatter, RuleRegistry, PluginManager } from '@codewaveinnovation/formatter';
import { CorePlugin } from '@codewaveinnovation/formatter';
import { MiPlugin } from './MiPlugin';
// Crear configuración personalizada
const registry = new RuleRegistry();
const pluginManager = new PluginManager(registry);
// Cargar plugin core (requerido para reglas estándar)
pluginManager.loadPlugin(new CorePlugin());
// Cargar plugins personalizados
pluginManager.loadPlugin(new MiPlugin());
// Crear formateador con registry personalizado
const formatter = new CodeFormatter(registry);Mejores Prácticas de Plugins
1. Responsabilidad Única
Cada plugin debe tener un propósito claro y enfocado:
// ✅ Bueno - Propósito enfocado
export class PluginFormatoStrings extends BasePlugin {
// Solo reglas relacionadas con strings
}
// ❌ Malo - Demasiado amplio
export class PluginTodo extends BasePlugin {
// 50 reglas diferentes no relacionadas
}2. Versionado
Siempre versiona tus plugins:
export class MiPlugin extends BasePlugin {
readonly name = 'mi-plugin';
readonly version = '1.2.0'; // Sigue versionado semántico
}3. Documentación
Documenta tus reglas con ejemplos claros:
/**
* Aplica espaciado consistente alrededor de operadores
*
* @example
* // Antes
* let x=1+2;
*
* // Después
* let x = 1 + 2;
*/
export class ReglaEspaciadoOperadores extends BaseFormattingRule {
// ...
}4. Opciones de Configuración
Haz las reglas configurables:
protected format(context: FormatContext): string {
const ruleConfig = context.config.rules.find(r => r.name === this.name);
const options = ruleConfig?.options || {
// Opciones por defecto
style: 'single',
avoidEscape: true,
};
// Usar opciones en tu lógica
}5. Manejo de Errores
Maneja casos extremos con gracia:
protected format(context: FormatContext): string {
const { content } = context;
// Manejar contenido vacío
if (!content || content.length === 0) {
return content;
}
try {
return this.transformContent(content);
} catch (error) {
// Registrar error pero no fallar
console.warn(`Regla ${this.name} falló:`, error);
return content; // Retornar contenido original
}
}Publicando Plugins
Estructura del Paquete
my-cwf-plugin/
├── src/
│ ├── rules/
│ │ ├── MiRegla.ts
│ │ └── OtraRegla.ts
│ ├── MiPlugin.ts
│ └── index.ts
├── package.json
├── tsconfig.json
└── README.mdpackage.json
{
"name": "@tuorg/cwf-plugin-nombre",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"peerDependencies": {
"@codewaveinnovation/formatter": "^1.0.0"
},
"keywords": ["cwf", "formatter", "plugin"]
}index.ts
// Exportar plugin y reglas
export { MiPlugin } from './MiPlugin';
export { MiRegla } from './rules/MiRegla';Publicando en npm
npm login
npm publish --access publicPlugins de la Comunidad
¿Quieres compartir tu plugin? ¡Envía un PR para agregarlo a esta lista!
Extensiones Oficiales
- @codewaveinnovation/formatter-eslint (próximamente) - Puente entre CWF y reglas de ESLint
- @codewaveinnovation/formatter-prettier (próximamente) - Reglas compatibles con Prettier
Plugins de la Comunidad
Aún no hay plugins de la comunidad - ¡sé el primero en crear uno!
Para agregar tu plugin:
- Haz fork del repositorio
- Agrega tu plugin a esta sección
- Incluye: nombre, descripción, link npm, link GitHub
- Envía un PR
Referencia de API de Plugins
BasePlugin
Clase abstracta para crear plugins.
Propiedades Requeridas:
name: string- Identificador único del pluginversion: string- Versión semver
Métodos Requeridos:
getRules(): IFormattingRule[]- Retorna array de reglas
PluginManager
Administra la carga de plugins y registro de reglas.
Métodos:
loadPlugin(plugin: IPlugin): void- Carga un plugin y registra sus reglasgetLoadedPlugins(): string[]- Retorna nombres de plugins cargados
RuleRegistry
Registro central para todas las reglas de formato.
Métodos:
register(rule: IFormattingRule): void- Registra una reglaget(name: string): IFormattingRule | undefined- Obtiene una regla por nombregetAll(): IFormattingRule[]- Obtiene todas las reglas registradashas(name: string): boolean- Verifica si existe una regla
Próximos Pasos
- Guía de Reglas - Aprende sobre las reglas integradas
- Configuración - Configura tu formateador
- Referencia de API - Documentación completa de la API
¿Necesitas Ayuda?
- 💬 Discusiones - Haz preguntas
- 🐛 Issues - Reporta bugs
- 📧 Contacto: support@codewaveinnovation.com