Update rules

This commit is contained in:
Alex
2024-08-25 16:28:34 -04:00
parent b0cc4a1525
commit 0138cabb27
18 changed files with 325 additions and 236 deletions

View File

@ -12,10 +12,10 @@
"subject": "chore(deps): update dependency node to v18.20.4 (#309)"
},
"eslint-plugin-jsx-a11y": {
"hash": "cca288b73a39fa0932a57c02a7a88de68fc971fc",
"date": "2024-07-22T02:39:43+01:00",
"hash": "a08fbcc502d6a6fa7d01a48c5f0b895c61e8cdd5",
"date": "2024-08-22T20:21:57+01:00",
"committer": "Jordan Harband",
"subject": "[readme] fix typo in shareable config section in readme"
"subject": "[Fix] `label-has-associated-control`: ignore undetermined label text"
},
"eslint-plugin-n": {
"hash": "e5e758ea0cd238220127ae7bcbd967f1d8920f28",

View File

@ -1,8 +1,9 @@
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { Rule } from 'eslint';
import noEmptyObjectLiteral from './no-empty-object-literal';
import noImportDot from './no-import-dot';
import noUselessImportAlias from './no-useless-import-alias';
import restrictTemplateExpressions from './restrict-template-expressions';
export const rules: Record<
@ -11,5 +12,6 @@ export const rules: Record<
> = {
'no-empty-object-literal': noEmptyObjectLiteral,
'no-import-dot': noImportDot,
'no-useless-import-alias': noUselessImportAlias,
'restrict-template-expressions': restrictTemplateExpressions,
};

View File

@ -0,0 +1,45 @@
import type { Rule } from 'eslint';
import type { Position } from 'estree';
const rule: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description:
"Ban useless import aliasing like `import { abc as abc } from 'module'`",
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
},
create(context) {
return {
ImportDeclaration(node) {
if (node.specifiers.length === 0) return;
for (const specifier of node.specifiers) {
if (specifier.type !== 'ImportSpecifier') continue;
const { imported, local } = specifier;
if (
imported.name === local.name &&
!arePositionsEqual(imported.loc!.start, local.loc!.start)
) {
context.report({
node: specifier,
message: `Useless aliasing of '${imported.name}'?`,
fix(fixer) {
return fixer.removeRange([imported.range![1], local.range![1]]);
},
});
}
}
},
};
},
};
const arePositionsEqual = (a: Position, b: Position) =>
a.line === b.line && a.column === b.column;
export default rule;

View File

@ -29,8 +29,6 @@ export default createRule<Option[], MessageId>({
type: 'problem',
docs: {
description: 'Enforce template literal expressions to be of `string` type',
recommended: 'recommended',
requiresTypeChecking: true,
},
messages: {
invalidType: 'Invalid type "{{type}}" of template literal expression.',
@ -55,6 +53,8 @@ export default createRule<Option[], MessageId>({
defaultOptions: [defaultOption],
create(context, [options]) {
const services = getParserServices(context);
if (!services.program) return {};
const checker = services.program.getTypeChecker();
const allowed = new Set(options.allow);

View File

@ -3,7 +3,7 @@ import { resolve } from 'node:path';
import type { Middleware } from './middleware';
import { jsdoc } from './presets/jsdoc';
import { reactQuery, storybook } from './presets/misc';
import { reactQuery, storybook, vitest } from './presets/misc';
import { react, reactRefresh } from './presets/react';
import { tailwind } from './presets/tailwind';
import { testingLibrary } from './presets/testing-library';
@ -16,6 +16,7 @@ const middlewares = {
reactQuery,
testingLibrary,
jsdoc,
vitest,
};
export const envs: {
@ -29,7 +30,6 @@ export const envs: {
},
{
dependency: '@vitejs/plugin-react',
eslintPlugin: 'eslint-plugin-react-refresh',
middleware: 'reactRefresh',
},
{
@ -52,6 +52,11 @@ export const envs: {
eslintPlugin: 'eslint-plugin-testing-library',
middleware: 'testingLibrary',
},
{
dependency: 'vitest',
eslintPlugin: 'eslint-plugin-vitest',
middleware: 'vitest',
},
];
export function getProjectDependencies() {

View File

@ -50,6 +50,8 @@ export interface LocalRuleOptions {
'custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
'custom/no-empty-object-literal': RuleEntry<unknown>;
/** Ban useless import alias */
'custom/no-useless-import-alias': RuleEntry<unknown>;
}
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
@ -76,6 +78,8 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
* Note this must be a string literal or an array of string literals since
* this is statically analyzed.
*
* Rules are prefixed with `custom/` and the file name is used as the rule name.
*/
customRuleFiles?: string | string[];
@ -161,7 +165,7 @@ export function extendConfig(
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
...(overrides ?? []),
],
rules: { ...eslintRules, ...rules },
rules: { ...eslintRules },
...rest,
};
@ -181,5 +185,7 @@ export function extendConfig(
result.plugins = unique(result.plugins);
result.extends = unique(result.extends);
Object.assign(result.rules, rules);
return result;
}

View File

@ -21,6 +21,7 @@ const transpilers = [
function tryRequire() {
for (const candidate of transpilers) {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require(candidate);
return;
} catch {}
@ -70,6 +71,7 @@ function main() {
if (!isAbsolute(file)) {
file = resolve(rootDir, file);
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
const module = unwrapDefault(require(file));
const name = module.name ?? basename(file, extname(file));
plugin.rules![name] = module;

View File

@ -4,9 +4,15 @@ import { defineMiddleware } from '../middleware';
const customRules: Partial<LocalRuleOptions> = {
'custom/no-import-dot': error,
'custom/restrict-template-expressions': error,
'custom/no-useless-import-alias': error,
};
export const custom = defineMiddleware((_, { addRules }) => {
export const custom = defineMiddleware((config, { addRules }) => {
addRules(customRules);
config.overrides.push({
files: ['*.ts', '!*.d.ts'],
rules: {
'custom/restrict-template-expressions': error,
},
});
});

View File

@ -7,3 +7,7 @@ export const storybook = defineMiddleware(config => {
export const reactQuery = defineMiddleware(config => {
config.extends.push('plugin:@tanstack/eslint-plugin-query/recommended');
});
export const vitest = defineMiddleware(config => {
config.extends.push('plugin:vitest/recommended');
});

View File

@ -7,22 +7,13 @@ import { defineMiddleware } from '../middleware';
const importRules: Partial<ImportXRulesObject> = {
'import-x/first': error,
'import-x/no-absolute-path': error,
'import-x/no-duplicates': error,
'import-x/no-duplicates': warn,
'import-x/no-useless-path-segments': error,
'import-x/order': [
error,
warn,
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
'type',
],
'newlines-between': 'always-and-inside-groups',
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
'newlines-between': 'always',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
@ -83,18 +74,19 @@ export const importTypeScript = defineMiddleware((config, { addRules, addSetting
});
config.overrides.push(
{
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
files: ['.eslintrc.js', '*.config.js', '*.cjs', '*.mjs'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'import-x/no-commonjs': off,
'import-x/unambiguous': off,
'@typescript-eslint/no-require-imports': off,
'rules/restrict-template-expressions': off,
},
},
{
files: ['*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-imports': off,
'import-x/unambiguous': off,
},
},
);

View File

@ -1,7 +1,7 @@
/* eslint-disable unicorn/string-content */
import type { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
import { error, warn } from '../constants';
import { error, off, warn } from '../constants';
import { defineMiddleware } from '../middleware';
const suggest = (suggest: string) => ({ suggest, fix: false });
@ -86,10 +86,13 @@ const unicornRules: Partial<UnicornRulesObject> = {
'<=>': suggest('⇔'),
'\\.\\.\\.': suggest('…'),
"'s ": suggest('s '),
"'d ": suggest('d '),
"'t ": suggest('t '),
"l'": suggest('l'),
"d'": suggest('d'),
'?!': suggest(''),
'!?': suggest(''),
"qu'": suggest('qu'),
'\\?!': suggest(''),
'!\\?': suggest('⁉'),
},
},
],
@ -99,4 +102,10 @@ const unicornRules: Partial<UnicornRulesObject> = {
export const unicorn = defineMiddleware((config, { addRules }) => {
config.plugins.push('unicorn');
addRules(unicornRules);
config.overrides.push({
files: ['*.test.ts', '*.test.tsx'],
rules: {
'unicorn/no-useless-undefined': off,
},
});
});