Remove member-ordering and complexity eslint-disable comments by reordering class members and applying targeted fixes. Add metoyou/no-maybe-in-naming, type-safe WebRTC e2e harness helpers, and resolve remaining lint errors so npm run lint exits cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
180 lines
5.9 KiB
JavaScript
180 lines
5.9 KiB
JavaScript
// Custom ESLint rules shared across the workspace.
|
||
|
||
const FORBIDDEN_UNICODE_SYMBOLS = Object.freeze([
|
||
{ char: '\u2013', name: 'Unicode en dash (–)', replacement: '-' },
|
||
{ char: '\u2014', name: 'Unicode em dash (—)', replacement: '-' },
|
||
{ char: '\u2026', name: 'Unicode ellipsis (…)', replacement: '...' },
|
||
{ char: '\u2192', name: 'Unicode right arrow (→)', replacement: '->' },
|
||
{ char: '\u2190', name: 'Unicode left arrow (←)', replacement: '<-' },
|
||
{ char: '\u2194', name: 'Unicode left-right arrow (↔)', replacement: '<->' },
|
||
{ char: '\u21d2', name: 'Unicode right double arrow (⇒)', replacement: '=>' },
|
||
{ char: '\u21d0', name: 'Unicode left double arrow (⇐)', replacement: '<=' },
|
||
{ char: '\u21d4', name: 'Unicode left-right double arrow (⇔)', replacement: '<=>' }
|
||
]);
|
||
|
||
function createReplaceFix(range, replacement) {
|
||
return (fixer) => fixer.replaceTextRange(range, replacement);
|
||
}
|
||
|
||
const MAYBE_PATTERN = /maybe/i;
|
||
|
||
function identifierContainsMaybe(name) {
|
||
return typeof name === 'string' && MAYBE_PATTERN.test(name);
|
||
}
|
||
|
||
function checkIdentifier(context, node, name) {
|
||
if (!identifierContainsMaybe(name))
|
||
return;
|
||
|
||
context.report({
|
||
node,
|
||
messageId: 'noMaybeInNaming',
|
||
data: { name }
|
||
});
|
||
}
|
||
|
||
module.exports = {
|
||
rules: {
|
||
'angular-template-spacing': {
|
||
meta: {
|
||
type: 'layout',
|
||
docs: {
|
||
description: 'Enforce spacing between elements and property grouping in Angular templates',
|
||
category: 'Stylistic Issues',
|
||
recommended: true
|
||
},
|
||
fixable: 'whitespace',
|
||
schema: []
|
||
},
|
||
create() {
|
||
// This is a placeholder for custom rule implementation.
|
||
// ESLint's template rules are limited, so manual formatting is recommended.
|
||
return {};
|
||
}
|
||
},
|
||
'no-maybe-in-naming': {
|
||
meta: {
|
||
type: 'suggestion',
|
||
docs: {
|
||
description: 'Disallow the word "maybe" in identifiers (variables, functions, classes, parameters)'
|
||
},
|
||
schema: [],
|
||
messages: {
|
||
noMaybeInNaming: 'Identifier "{{name}}" must not contain "maybe". Use an explicit name that states intent.'
|
||
}
|
||
},
|
||
create(context) {
|
||
const reportDefinition = (node, name) => checkIdentifier(context, node, name);
|
||
|
||
return {
|
||
VariableDeclarator(node) {
|
||
if (node.id.type === 'Identifier')
|
||
reportDefinition(node.id, node.id.name);
|
||
},
|
||
FunctionDeclaration(node) {
|
||
if (node.id)
|
||
reportDefinition(node.id, node.id.name);
|
||
},
|
||
ClassDeclaration(node) {
|
||
if (node.id)
|
||
reportDefinition(node.id, node.id.name);
|
||
},
|
||
MethodDefinition(node) {
|
||
if (node.key.type === 'Identifier')
|
||
reportDefinition(node.key, node.key.name);
|
||
},
|
||
PropertyDefinition(node) {
|
||
if (node.key.type === 'Identifier')
|
||
reportDefinition(node.key, node.key.name);
|
||
},
|
||
TSInterfaceDeclaration(node) {
|
||
reportDefinition(node.id, node.id.name);
|
||
},
|
||
TSTypeAliasDeclaration(node) {
|
||
reportDefinition(node.id, node.id.name);
|
||
},
|
||
TSMethodSignature(node) {
|
||
if (node.key.type === 'Identifier')
|
||
reportDefinition(node.key, node.key.name);
|
||
},
|
||
TSPropertySignature(node) {
|
||
if (node.key.type === 'Identifier')
|
||
reportDefinition(node.key, node.key.name);
|
||
},
|
||
TSParameterProperty(node) {
|
||
if (node.parameter.type === 'Identifier')
|
||
reportDefinition(node.parameter, node.parameter.name);
|
||
},
|
||
'FunctionDeclaration > Identifier[id]': function onParam(node) {
|
||
reportDefinition(node, node.name);
|
||
},
|
||
'FunctionExpression > Identifier[id]': function onParam(node) {
|
||
reportDefinition(node, node.name);
|
||
},
|
||
'ArrowFunctionExpression > Identifier[id]': function onParam(node) {
|
||
reportDefinition(node, node.name);
|
||
}
|
||
};
|
||
}
|
||
},
|
||
'no-unicode-symbols': {
|
||
meta: {
|
||
type: 'suggestion',
|
||
docs: {
|
||
description: 'Disallow AI/LLM-style Unicode symbols in source files'
|
||
},
|
||
fixable: 'code',
|
||
hasSuggestions: true,
|
||
schema: [],
|
||
messages: {
|
||
forbiddenSymbol: '{{name}} is not allowed. Use ASCII "{{replacement}}" instead.',
|
||
replaceSymbol: 'Replace with "{{replacement}}"'
|
||
}
|
||
},
|
||
create(context) {
|
||
const sourceCode = context.getSourceCode();
|
||
|
||
return {
|
||
Program() {
|
||
const sourceText = sourceCode.getText();
|
||
|
||
for (const symbol of FORBIDDEN_UNICODE_SYMBOLS) {
|
||
let index = sourceText.indexOf(symbol.char);
|
||
|
||
while (index !== -1) {
|
||
const range = [index, index + symbol.char.length];
|
||
const loc = {
|
||
start: sourceCode.getLocFromIndex(range[0]),
|
||
end: sourceCode.getLocFromIndex(range[1])
|
||
};
|
||
|
||
context.report({
|
||
loc,
|
||
messageId: 'forbiddenSymbol',
|
||
data: {
|
||
name: symbol.name,
|
||
replacement: symbol.replacement
|
||
},
|
||
fix: createReplaceFix(range, symbol.replacement),
|
||
suggest: [
|
||
{
|
||
messageId: 'replaceSymbol',
|
||
data: {
|
||
replacement: symbol.replacement
|
||
},
|
||
fix: createReplaceFix(range, symbol.replacement)
|
||
}
|
||
]
|
||
});
|
||
|
||
index = sourceText.indexOf(symbol.char, index + symbol.char.length);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
}
|
||
},
|
||
FORBIDDEN_UNICODE_SYMBOLS
|
||
};
|