/**
* Breeze Theme Coding Standards Preset
* Technology: Vanilla JS + Breeze component API + lightweight LESS
* Breeze replaces RequireJS and heavy jQuery with a lightweight vanilla JS approach.
* Reference: https://breezefront.com/
*/
import { ThemeStandard } from '../types.js';
export const BREEZE_THEME: ThemeStandard = {
id: 'breeze',
name: 'Breeze Theme',
version: '1.0.0',
description: 'Standards for Breeze-based Magento themes. Breeze removes RequireJS and replaces heavy jQuery usage with a lightweight vanilla JS approach and its own component API.',
technologies: {
use: [
'Breeze component API ($.widget, $.view, $.mixin)',
'Vanilla JavaScript (document.querySelector, fetch)',
'Lightweight jQuery-compatible API (Breeze provides a jQuery-like subset)',
'LESS preprocessor (lightweight usage)',
'data-mage-init (Breeze intercepts and processes these)',
'Breeze turbo mode (SPA-like navigation)',
'ES6+ syntax (arrow functions, template literals, destructuring)',
'Intersection Observer for lazy loading',
],
avoid: [
'RequireJS (define(), require())',
'Heavy jQuery plugins that Breeze does not include',
'KnockoutJS heavy usage (Breeze provides a minimal compatibility layer)',
'Magento UI Components (uiComponent) — use Breeze views instead',
'jQuery UI widgets not ported to Breeze',
'Large third-party JS libraries',
],
},
validationRules: [
{
pattern: '\\bdefine\\s*\\(\\s*\\[|\\brequire\\s*\\(\\s*\\[',
fileTypes: ['js'],
severity: 8,
type: 'warning',
message: 'RequireJS (define/require) detected. Breeze does not use RequireJS. Use Breeze component registration.',
rule: 'Breeze.JS.NoRequireJS',
suggestion: 'Use $.widget(), $.view(), or plain JS. Register components via breeze registry instead of RequireJS.',
mode: 'discourage',
},
{
pattern: 'uiComponent|Magento_Ui/js/lib',
fileTypes: ['js'],
severity: 7,
type: 'warning',
message: 'Magento UI Component detected. Breeze uses its own lightweight view system instead of uiComponent.',
rule: 'Breeze.JS.NoUIComponent',
suggestion: 'Use $.view() for component-like behavior in Breeze. It provides similar functionality with less overhead.',
mode: 'discourage',
},
{
pattern: '\\$\\.ajax\\s*\\(|jQuery\\.ajax\\s*\\(',
fileTypes: ['js'],
severity: 6,
type: 'warning',
message: 'jQuery.ajax() detected. Prefer the native fetch() API in Breeze for HTTP requests.',
rule: 'Breeze.JS.PreferFetch',
suggestion: 'Use fetch() with async/await: const response = await fetch(url); const data = await response.json();',
mode: 'discourage',
},
{
pattern: 'ko\\.observable|ko\\.computed|ko\\.applyBindings',
fileTypes: ['js'],
severity: 7,
type: 'warning',
message: 'KnockoutJS API detected. Breeze provides only a minimal KO compatibility layer. Prefer Breeze views.',
rule: 'Breeze.JS.MinimalKnockout',
suggestion: 'Use $.view() with Breeze reactive properties instead of KnockoutJS observables.',
mode: 'discourage',
},
],
patternOverrides: [
{
taskKeyword: 'requirejs module',
correctPattern: 'Use Breeze component registration (no RequireJS)',
example: `// Breeze: Component Registration
// File: app/code/Vendor/Module/view/frontend/web/js/breeze/my-component.js
// Breeze components are registered globally — no define() needed
(function () {
'use strict';
$.widget('vendorMyComponent', {
component: 'Vendor_Module/js/my-component',
options: {
selector: '.item',
activeClass: 'active'
},
create: function () {
this.element.on('click', this.options.selector, this.onItemClick.bind(this));
},
onItemClick: function (event) {
event.preventDefault();
$(event.currentTarget).toggleClass(this.options.activeClass);
},
destroy: function () {
this.element.off('click');
this._super();
}
});
})();
// Usage in phtml (same as Luma — Breeze intercepts data-mage-init):
// <div data-mage-init='{"Vendor_Module/js/my-component": {"selector": ".item"}}'>`,
avoidPatterns: [
{ pattern: 'define([], function() {})', reason: 'Breeze does not use RequireJS' },
{ pattern: 'require([], function() {})', reason: 'Breeze loads JS directly, no AMD' },
],
explanation: 'Breeze components are registered via $.widget() or $.view() in IIFE wrappers. The "component" property maps to the same path used in data-mage-init, so existing templates work without changes.',
},
{
taskKeyword: 'jquery widget',
correctPattern: 'Use Breeze $.widget() — similar API to jQuery UI but lightweight',
example: `// Breeze: Lightweight Widget
(function () {
'use strict';
$.widget('vendorCounter', {
component: 'Vendor_Module/js/counter',
options: {
initialCount: 0,
maxCount: 100
},
create: function () {
this.count = this.options.initialCount;
this.display = this.element.find('[data-role="count"]');
this.updateDisplay();
this.element.on('click', '[data-action="increment"]', this.increment.bind(this));
this.element.on('click', '[data-action="decrement"]', this.decrement.bind(this));
},
increment: function () {
if (this.count < this.options.maxCount) {
this.count++;
this.updateDisplay();
}
},
decrement: function () {
if (this.count > 0) {
this.count--;
this.updateDisplay();
}
},
updateDisplay: function () {
this.display.text(this.count);
}
});
})();`,
avoidPatterns: [
{ pattern: 'jquery-ui-modules/widget', reason: 'Breeze provides its own widget factory' },
{ pattern: '_create / _init / _destroy', reason: 'Breeze uses create() and destroy() (no underscore prefix)' },
],
explanation: 'Breeze $.widget() is similar to jQuery UI but lighter. Key differences: use create() instead of _create(), destroy() instead of _destroy(). The "component" property enables data-mage-init mapping.',
},
{
taskKeyword: 'template structure',
correctPattern: 'Use $block, $escaper, data-mage-init (Breeze intercepts these)',
example: `<?php
declare(strict_types=1);
use Magento\\Framework\\Escaper;
use Magento\\Framework\\View\\Element\\Template;
/** @var Template $block */
/** @var Escaper $escaper */
$viewModel = $block->getData('viewModel');
?>
<!-- Breeze: Templates are compatible with Luma data-mage-init -->
<div class="my-component"
data-mage-init='{"Vendor_Module/js/my-component": {
"apiUrl": "<?= $escaper->escapeUrl($viewModel->getApiUrl()) ?>"
}}'>
<h2 class="my-component-title">
<?= $escaper->escapeHtml($viewModel->getTitle()) ?>
</h2>
<?php foreach ($viewModel->getItems() as $item): ?>
<div class="my-component-item" data-id="<?= $escaper->escapeHtmlAttr($item->getId()) ?>">
<span><?= $escaper->escapeHtml($item->getName()) ?></span>
<button data-action="toggle" type="button">
<?= $escaper->escapeHtml(__('Toggle')) ?>
</button>
</div>
<?php endforeach; ?>
</div>`,
avoidPatterns: [
{ pattern: 'x-magento-init with RequireJS paths', reason: 'Breeze intercepts data-mage-init; no RequireJS involved' },
],
explanation: 'Breeze templates are largely compatible with Luma templates. data-mage-init works the same way because Breeze intercepts these and maps them to its own component registry. LESS and class names stay the same.',
},
],
bestPractices: [
'Always use declare(strict_types=1) at the top of phtml files',
'Register Breeze components in web/js/breeze/ directory',
'Use the "component" property in $.widget() to map to data-mage-init paths',
'Use create() and destroy() methods (not _create/_destroy)',
'Use native fetch() instead of $.ajax() for HTTP requests',
'Use ES6+ syntax: arrow functions, template literals, const/let',
'Keep JS bundles small — Breeze is optimized for performance',
'Use Intersection Observer for lazy-loading content',
'Test with Breeze turbo mode enabled (SPA-like navigation)',
'Use $escaper for all output escaping (same as base Magento)',
'Breeze provides jQuery-like $() — use it for DOM queries',
'Register mixins with $.mixin() to extend existing components',
],
};