80 lines
2.5 KiB
JavaScript
80 lines
2.5 KiB
JavaScript
/**
|
|
* ESLint rule to forbid raw HTML in app/ directory
|
|
*
|
|
* All HTML must be encapsulated in React components from components/ or ui/
|
|
*
|
|
* Rationale:
|
|
* - app/ should only contain page/layout components
|
|
* - Raw HTML with styling violates separation of concerns
|
|
* - UI logic belongs in components/ui layers
|
|
*/
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Forbid raw HTML with styling in app/ directory',
|
|
category: 'Architecture',
|
|
recommended: true,
|
|
},
|
|
fixable: null,
|
|
schema: [],
|
|
messages: {
|
|
noRawHtml: 'Raw HTML with styling is forbidden in app/. Use a component from components/ or ui/.',
|
|
},
|
|
},
|
|
|
|
create(context) {
|
|
const filename = context.getFilename();
|
|
const isInApp = filename.includes('/app/');
|
|
|
|
if (!isInApp) return {};
|
|
|
|
// HTML tags that should be wrapped in components
|
|
const htmlTags = [
|
|
'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
'p', 'button', 'input', 'form', 'label', 'select', 'textarea',
|
|
'ul', 'ol', 'li', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
|
|
'section', 'article', 'header', 'footer', 'nav', 'aside',
|
|
'main', 'aside', 'figure', 'figcaption', 'blockquote', 'code',
|
|
'pre', 'a', 'img', 'svg', 'path', 'g', 'rect', 'circle'
|
|
];
|
|
|
|
return {
|
|
JSXElement(node) {
|
|
const openingElement = node.openingElement;
|
|
|
|
if (openingElement.name.type !== 'JSXIdentifier') return;
|
|
|
|
const tagName = openingElement.name.name;
|
|
|
|
// Check if it's a raw HTML element (lowercase)
|
|
if (htmlTags.includes(tagName) && tagName[0] === tagName[0].toLowerCase()) {
|
|
|
|
// Check for styling attributes
|
|
const hasClassName = openingElement.attributes.some(
|
|
attr => attr.type === 'JSXAttribute' && attr.name.name === 'className'
|
|
);
|
|
const hasStyle = openingElement.attributes.some(
|
|
attr => attr.type === 'JSXAttribute' && attr.name.name === 'style'
|
|
);
|
|
|
|
// Check for inline event handlers (also a concern)
|
|
const hasInlineHandlers = openingElement.attributes.some(
|
|
attr => attr.type === 'JSXAttribute' &&
|
|
attr.name.name &&
|
|
attr.name.name.startsWith('on')
|
|
);
|
|
|
|
if (hasClassName || hasStyle || hasInlineHandlers) {
|
|
context.report({
|
|
node,
|
|
messageId: 'noRawHtml',
|
|
});
|
|
}
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|