feat: rules variations

This commit is contained in:
Guz013
2023-09-02 08:19:33 -03:00
parent ef6b399bb7
commit ac7c70e81c
14 changed files with 218 additions and 113 deletions

View File

@@ -44,6 +44,7 @@ const config = {
node: true,
typescript: true,
},
'jsdoc/mode': 'typescript',
},
};
export default config;

View File

@@ -1,9 +1,10 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
/** @type {import('eslint').Linter.FlatConfig} */
const recommended = {
/** @type {import('./index.d.ts').ConfigVariations} */
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
...{}, // Plugin: eslint-plugin-jsdoc
@@ -14,19 +15,19 @@ const recommended = {
'jsdoc/require-property-description': 'error',
'jsdoc/require-returns-check': 'error',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const strict = {
...recommended,
/** @type {import('./index.d.ts').ConfigVariations} */
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
...{}, // Plugin: eslint-plugin-jsdoc
'jsdoc/require-description': 'error',
'jsdoc/require-file-overview': 'error',
},
};
});
const documentation = { recommended, strict };
export default documentation;

View File

@@ -1,9 +1,10 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../../constants.js';
/** @type {import('eslint').Linter.FlatConfig} */
const recommended = {
/** @type {import('../index.d.ts').ConfigVariations} */
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
...{}, // Plugin: eslint-plugin-unicorn
@@ -16,15 +17,15 @@ const recommended = {
'unicorn/prefer-modern-dom-apis': 'error',
'unicorn/prefer-query-selector': 'error',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const strict = {
...recommended,
/** @type {import('../index.d.ts').ConfigVariations} */
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
},
};
});
const node = { recommended, strict };
export default node;

View File

@@ -1,9 +1,10 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../../constants.js';
/** @type {import('eslint').Linter.FlatConfig} */
const commonjs = {
/** @type {import('../index.d.ts').ConfigVariations} */
const commonjs = createVariations({
files: ['**/*.cts', '**/*.cjs'],
rules: {
...{}, // Plugin: @typescript-eslint/eslint-plugin
@@ -16,10 +17,10 @@ const commonjs = {
...{}, // Plugin: eslint-plugin-import
'import/no-commonjs': 'off',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const recommended = {
/** @type {import('../index.d.ts').ConfigVariations} */
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
...{}, // Plugin: eslint-plugin-unicorn
@@ -28,15 +29,15 @@ const recommended = {
...{}, // Plugin: eslint-plugin-import
'import/no-dynamic-require': 'error',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const strict = {
...recommended,
/** @type {import('../index.d.ts').ConfigVariations} */
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
},
};
});
const node = { commonjs, recommended, strict };
export default node;

View File

@@ -1,14 +1,15 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
import perfectionistPlugin from 'eslint-plugin-perfectionist';
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
/**
* This config relates to code formatting and style in JavaScript and TypeScript
* Recommended alternative, better for projects in prototyping phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const recommended = {
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
plugins: {
// @ts-expect-error because plugin doesn't export correct type
@@ -107,6 +108,9 @@ const recommended = {
'@typescript-eslint/type-annotation-spacing': ['error', {
after: true,
before: false,
overrides: {
arrow: { after: true, before: true },
},
}],
'block-spacing': 'off',
'brace-style': 'off',
@@ -149,19 +153,19 @@ const recommended = {
'perfectionist/sort-union-types': ['error', { type: 'natural' }],
},
};
});
/**
* This config relates to code formatting and style in JavaScript and TypeScript
* Strict alternative, better for projects in refactoring and/or production phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const strict = {
...recommended,
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
},
};
});
const formatting = { recommended, strict };
export default formatting;

View File

@@ -1,41 +1,58 @@
import type { Linter } from 'eslint';
interface ConfigVariations {
/**
* Enable all rules with `error` level.
*/
error: Linter.FlatConfig,
/**
* Disable all rules in this config.
*/
off: Linter.FlatConfig,
/**
* Enable all rules with `warn` level.
*/
warn: Linter.FlatConfig,
}
const configs: Readonly<{
core: Linter.FlatConfig,
documentation: {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
environments: {
browser: {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
node: {
commonjs: Linter.FlatConfig,
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
commonjs: ConfigVariations,
recommended: ConfigVariations,
strict: ConfigVariations,
},
},
formatting: {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
naming: {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
overrides: {
performance: Linter.FlatConfig,
'inferrable-types': ConfigVariations,
performance: ConfigVariations,
},
suggestions: {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
'suggestions-typescript': {
recommended: Linter.FlatConfig,
strict: Linter.FlatConfig,
recommended: ConfigVariations,
strict: ConfigVariations,
},
}>;
export default configs;
export type { ConfigVariations };

View File

@@ -1,30 +1,31 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
/**
* This config suggest alternate ways of doing things in JavaScript and TypeScript
* Recommended alternative, better for projects in prototyping phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const recommended = {
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
...{}, // Plugin: eslint-plugin-unicorn
'unicorn/filename-case': ['error', { case: 'kebabCase' }],
'unicorn/prevent-abbreviations': 'error',
},
};
});
/**
* This config suggest alternate ways of doing things in JavaScript and TypeScript
* Strict alternative, better for projects in refactoring and/or production phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const strict = {
...recommended,
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
...{}, // Plugin: @typescript-eslint/eslint-plugin
// '@typescript-eslint/naming-convention': 'error',
@@ -32,7 +33,7 @@ const strict = {
'unicorn/no-keyword-prefix': 'error',
},
};
});
const suggestions = { recommended, strict };
export default suggestions;

View File

@@ -1,25 +1,27 @@
// eslint-disable-next-line import/no-relative-parent-imports
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
// TODO [>=1.0.0]: Create a separate config for performance related practices
/** @type {import('eslint').Linter.FlatConfig} */
const performance = {
/** @type {import('./index.d.ts').ConfigVariations} */
const performance = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
'prefer-object-spread': 'off',
'prefer-spread': 'off',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const inferrableTypes = {
/** @type {import('./index.d.ts').ConfigVariations} */
const inferrableTypes = createVariations({
files: [...tsFiles],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/typedef': 'off',
},
};
});
const overrides = { inferrableTypes, performance };
const overrides = { 'inferrable-types': inferrableTypes, performance };
export default overrides;

View File

@@ -1,13 +1,14 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
/**
* This config relates to possible logic and syntax errors JavaScript and TypeScript
* Recommended alternative, better for projects in prototyping phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const recommended = {
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
...{}, // ESLint rules
@@ -75,17 +76,17 @@ const recommended = {
'import/namespace': 'error',
'import/no-unresolved': 'error',
},
};
});
/**
* This config relates to possible logic and syntax errors JavaScript and TypeScript
* Strict alternative, better for projects in refactoring and/or production phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const strict = {
...recommended,
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
...{}, // ESLint rules
'no-constant-binary-expression': 'error',
@@ -102,7 +103,7 @@ const strict = {
...{}, // Plugin: eslint-plugin-import
'import/no-extraneous-dependencies': 'error',
},
};
});
const problems = { recommended, strict };
export default problems;

View File

@@ -1,10 +1,10 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
// eslint-disable-next-line import/no-relative-parent-imports
import { tsFiles } from '../constants.js';
/** @type {import('eslint').Linter.FlatConfig} */
const recommended = {
/** @type {import('./index.d.ts').ConfigVariations} */
const recommended = createVariations({
files: [...tsFiles],
rules: {
...{}, // Plugin: @typescript-eslint/eslint-plugin
@@ -17,18 +17,18 @@ const recommended = {
'jsdoc/require-property-type': 'off',
'jsdoc/require-returns-type': 'off',
},
};
});
/** @type {import('eslint').Linter.FlatConfig} */
const strict = {
...recommended,
/** @type {import('./index.d.ts').ConfigVariations} */
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
...{}, // Plugin: @typescript-eslint/eslint-plugin
'@typescript-eslint/explicit-member-accessibility': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'error',
},
};
});
const typescript = { recommended, strict };
export default typescript;

View File

@@ -1,13 +1,15 @@
/* eslint-disable import/no-relative-parent-imports */
/* eslint-disable unicorn/no-useless-spread */
// eslint-disable-next-line import/no-relative-parent-imports
import { createVariations } from '../lib/rule-variations.js';
import { jsFiles, tsFiles } from '../constants.js';
/**
* This config suggest alternate ways of doing things in JavaScript and TypeScript
* Recommended alternative, better for projects in prototyping phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const recommended = {
const recommended = createVariations({
files: [...tsFiles, ...jsFiles],
rules: {
'camelcase': 'error',
@@ -242,20 +244,20 @@ const recommended = {
'jsdoc/require-yields': 'error',
'jsdoc/require-yields-check': 'error',
'jsdoc/sort-tags': 'error',
'jsdoc/valid-types': 'error',
// 'jsdoc/valid-types': 'error', This is already handled by Typescript type checking mostly
},
};
});
/**
* This config suggest alternate ways of doing things in JavaScript and TypeScript
* Strict alternative, better for projects in refactoring and/or production phases.
* @type {import('eslint').Linter.FlatConfig}
* @type {import('./index.d.ts').ConfigVariations}
*/
const strict = {
...recommended,
const strict = createVariations({
...recommended.error,
rules: {
...recommended.rules,
...recommended.error.rules,
...{}, // ESLint rules
'accessor-pairs': 'error',
@@ -363,7 +365,6 @@ const strict = {
'@typescript-eslint/parameter-properties': 'error',
'@typescript-eslint/prefer-enum-initializers': 'error',
'@typescript-eslint/prefer-readonly': 'error',
'@typescript-eslint/prefer-readonly-parameter-types': 'error',
'@typescript-eslint/promise-function-async': 'error',
'@typescript-eslint/require-array-sort-compare': 'error',
'@typescript-eslint/require-await': 'error',
@@ -428,7 +429,7 @@ const strict = {
'jsdoc/no-blank-blocks': 'error',
},
};
});
const suggestions = { recommended, strict };
export default suggestions;

View File

@@ -0,0 +1,75 @@
/**
* @typedef {import('eslint').Linter.RuleEntry} RuleEntry
* @typedef {import('eslint').Linter.RuleLevel} RuleLevel
* @typedef {import('eslint').Linter.FlatConfig} FlatConfig
*/
/**
* @param {Readonly<RuleEntry>} ruleEntry
* - The rule entry to be modified.
* @param {RuleLevel} level
* - The new level to be passed to the rule.
* @returns {RuleEntry}
*/
function changeLevel(ruleEntry, level) {
if (typeof level === 'number') {
/** @type {RuleLevel[]} */
const levels = ['error', 'off', 'warn'];
level = levels[level];
}
if (Array.isArray(ruleEntry))
return [level, ruleEntry[1]];
return level;
}
/**
* @param {Readonly<{[name: string]: RuleEntry}>} rules
* - The object to be iterated through.
* @param {([name, entry]: [string, RuleEntry]) => [string, RuleEntry]} handler
* - Function to run on every rule entry.
* @returns {{[name: string]: RuleEntry}} - The resulting object.
*/
function iterateRules(rules, handler) {
const entries = Object.entries(rules);
entries.map(entry => handler(entry));
return Object.fromEntries(entries);
}
/**
* @param {Readonly<FlatConfig>} config
* - The configuration object to create `error`, `warn`, and `off` variations.
* @returns {{error: FlatConfig, warn: FlatConfig, off: FlatConfig}}
*/
function createVariations(config) {
const configError = {
...config,
rules: iterateRules(
config.rules ?? {},
([key, entry]) => [key, changeLevel(entry, 'error')],
),
};
const configWarning = {
...config,
rules: iterateRules(
config.rules ?? {},
([key, entry]) => [key, changeLevel(entry, 'warn')],
),
};
const configDisabled = {
...config,
rules: iterateRules(
config.rules ?? {},
([key, entry]) => [key, changeLevel(entry, 'off')],
),
};
return { error: configError, off: configDisabled, warn: configWarning };
}
export { createVariations, iterateRules };

View File

@@ -4,10 +4,10 @@ import configs from '../configs/index.js';
/** @type {import('eslint').Linter.FlatConfig[]} */
const recommended = [
configs.core,
configs['suggestions-typescript'].recommended,
configs.suggestions.recommended,
configs.formatting.recommended,
configs.naming.recommended,
configs.documentation.recommended,
configs['suggestions-typescript'].recommended.error,
configs.suggestions.recommended.error,
configs.formatting.recommended.error,
configs.naming.recommended.error,
configs.documentation.recommended.error,
];
export default recommended;

View File

@@ -4,10 +4,10 @@ import configs from '../configs/index.js';
/** @type {import('eslint').Linter.FlatConfig[]} */
const strict = [
configs.core,
configs['suggestions-typescript'].strict,
configs.suggestions.strict,
configs.formatting.strict,
configs.naming.strict,
configs.documentation.recommended,
configs['suggestions-typescript'].strict.error,
configs.suggestions.strict.error,
configs.formatting.strict.error,
configs.naming.strict.error,
configs.documentation.recommended.error,
];
export default strict;