Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/linter/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import eslint from '@eslint/js';
import jsdoc from 'eslint-plugin-jsdoc';
import stylistic from '@stylistic/eslint-plugin';
import tseslint from 'typescript-eslint';
import v8IgnoreAssertGuardEquals from './customRules/v8IgnoreAssertGuardEquals.js';

/** Array of configuration objects merged together to form baseline for linting of the code. */
export const eslintConfig = defineConfig(
Expand All @@ -14,6 +15,11 @@ export const eslintConfig = defineConfig(
'languageOptions': { 'parserOptions': { 'projectService': true } },
'linterOptions': { 'reportUnusedDisableDirectives': true },
'plugins': {
'custom-rules': {
'rules': {
'v8-ignore-assert-guard-equals': v8IgnoreAssertGuardEquals
}
},
jsdoc,
stylistic
},
Expand Down Expand Up @@ -61,6 +67,7 @@ export const eslintConfig = defineConfig(
'consistent-return': 'off',
'consistent-this': 'warn',
'curly': 'warn',
'custom-rules/v8-ignore-assert-guard-equals': 'warn',
'default-case': 'warn',
'default-case-last': 'warn',
'default-param-last': 'off',
Expand Down Expand Up @@ -445,6 +452,7 @@ export const eslintConfig = defineConfig(
'eslint.config.mjs',
'eslint.config.js',
'bin/',
'coverage/',
'dist/'
])
);
114 changes: 114 additions & 0 deletions config/linter/customRules/v8IgnoreAssertGuardEquals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { type Rule, type SourceCode } from 'eslint';
import type { Comment } from 'estree';

/**
* Checks if the given comment is a legacy V8 ignore next comment that is adjacent to the given node.
* @param comment The comment to check.
* @param node The AST node to check against.
* @returns A boolean indicating whether the comment is a legacy V8 ignore next comment adjacent to the node.
*/
function isLegacyV8IgnoreNextComment(comment: Comment | undefined, node: Rule.Node): boolean {
if (!node.loc) {
return false;
}

return (
comment?.type === 'Block' &&
comment.value.trim() === 'v8 ignore next' &&
comment.loc?.end.line === node.loc.start.line - 1
);
}

/**
* Checks if the given node has a preceding comment that matches the V8 ignore directive.
* @param sourceCode The source code object provided by ESLint, which contains methods for accessing the code being linted and its comments.
* @param node The AST node to check for a preceding comment.
* @returns A boolean indicating whether the node has a preceding comment that matches the V8 ignore directive.
*/
function hasV8IgnoreNextComment(sourceCode: SourceCode, node: Rule.Node): boolean {
if (!node.loc || !node.range) {
return false;
}

/** The comment that exists on the immediate line before the inspected node. */
const precedingComment = sourceCode.getCommentsBefore(node).at(-1);

return (
precedingComment?.type === 'Block' &&
precedingComment.value.trim() === 'v8 ignore next -- @preserve' &&
precedingComment.loc?.end.line === node.loc.start.line - 1
);
}

/**
* Checks if the given node is a call expression to the function `assertGuardEquals`.
* @param node The AST node to check.
* @returns A boolean indicating whether the node is a call expression to `assertGuardEquals`.
*/
function isAssertGuardEquals(node: Rule.Node): boolean {
return node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
node.callee.name === 'assertGuardEquals';
}

const v8IgnoreAssertGuardEquals: Rule.RuleModule = {
/**
* Create is a method that defines the logic for the ESLint rule. It returns an object containing visitor methods that will be called during the traversal of the Abstract Syntax Tree (AST) of the code being linted.
* @param context The context object provided by ESLint, which contains information about the current state of the linting process and provides methods for reporting issues and accessing the source code.
* @returns The visitor object that will be used to traverse the AST and apply the rule logic.
*/
create(context: Rule.RuleContext) {
const { sourceCode } = context;

return {
/**
* CallExpression is a visitor method that will be called for every CallExpression node in the AST. It checks if the node is a call to `assertGuardEquals`
* and whether it has a preceding V8 ignore comment. If not, it reports an issue and provides a fixer to automatically add the required comment.
* @param node The CallExpression AST node being visited.
*/
CallExpression(node): void {
if (!isAssertGuardEquals(node)) { return; }

if (hasV8IgnoreNextComment(sourceCode, node)) { return; }

context.report({
/**
* Fixer function that defines how to automatically fix the reported issue. It takes a fixer object as an argument, which provides methods for creating and applying fixes to the source code.
* @param fixer The fixer object provided by ESLint, which contains methods for creating and applying fixes to the source code.
* @returns A fix object that describes the changes to be made to the source code in order to resolve the reported issue.
*/
fix(fixer) {
/** The comment to create. */
const comment = '/* v8 ignore next -- @preserve */\n';

/** The comment directly before the code being inspected. */
const precedingComment = sourceCode.getCommentsBefore(node).at(-1);

if (isLegacyV8IgnoreNextComment(precedingComment, node)) {
return fixer.replaceText(precedingComment, '/* v8 ignore next -- @preserve */');
}

return fixer.insertTextBefore(node, comment);
},
'messageId': 'wrap',
node
});
}
};
},
'meta': {
'dialects': ['typescript'],
'docs': {
'description': 'All assertGuardEquals calls should be ignored by V8.',
'recommended': false
},
'fixable': 'code',
'messages': {
'wrap': 'All assertGuardEquals calls should be ignored by V8.'
},
'type': 'problem'
}

};

export default v8IgnoreAssertGuardEquals;
Loading
Loading