mirror of
https://github.com/scratchfoundation/eslint-config-scratch.git
synced 2025-08-28 22:40:13 -04:00
fix: be more careful about ext => rule mapping
See comments about `no-undef`
This commit is contained in:
parent
a7dda101f2
commit
d01085335c
1 changed files with 164 additions and 65 deletions
229
lib/eslint.mjs
229
lib/eslint.mjs
|
@ -23,25 +23,74 @@ const legacy = {
|
||||||
react: legacyReact,
|
react: legacyReact,
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://www.npmjs.com/package/eslint-plugin-html#user-content-settings
|
// WARNING: eslint rules from `typescript-eslint`, even the "untyped" rules, assume that your code will be run through
|
||||||
const htmlExtensions = htmlSettings.getSettings({}).htmlExtensions
|
// `tsc` or equivalent for type checking. Using any rule set from `typescript-eslint` will, for example, turn off the
|
||||||
|
// `no-undef` rule. That makes sense if you'll use TypeScript to catch undefined globals, but it could be dangerous
|
||||||
// '.html' => '**/*.html'
|
// for plain JavaScript.
|
||||||
const htmlGlobs = htmlExtensions.map(ext => `**/*${ext}`)
|
// More information here: https://github.com/typescript-eslint/typescript-eslint/issues/8825#issuecomment-2033315610
|
||||||
|
|
||||||
const typeScriptExtensions = ['.ts', '.tsx', '.mts', '.cts']
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base rules recommended when type information is not available.
|
* Convert an array of file extensions to an array of globs
|
||||||
* These rules are also safe to use when type information is available.
|
* @param {string[]} extArray - an array of file extensions, like `.foo`
|
||||||
|
* @returns {string[]} an array of globs, like `** /*.foo` (without the space)
|
||||||
*/
|
*/
|
||||||
const typeFreeRules = tseslint.config(
|
const extArrayToGlobArray = extArray => extArray.map(ext => `**/*${ext}`)
|
||||||
eslint.configs.recommended,
|
|
||||||
tseslint.configs.recommended,
|
|
||||||
tseslint.configs.stylistic,
|
|
||||||
|
|
||||||
|
// See https://www.npmjs.com/package/eslint-plugin-html#user-content-settings
|
||||||
|
const htmlSettingsDefault = htmlSettings.getSettings({})
|
||||||
|
|
||||||
|
const fileExtensions = (x => {
|
||||||
|
x.allScript = [...x.javaScript, ...x.typeScript]
|
||||||
|
return x
|
||||||
|
})({
|
||||||
|
html: /** @type {string[]} */ (htmlSettingsDefault.htmlExtensions),
|
||||||
|
javaScript: ['.js', '.jsx', '.mjs', '.cjs'],
|
||||||
|
markdown: ['.md'],
|
||||||
|
typeScript: ['.ts', '.tsx', '.mts', '.cts'],
|
||||||
|
react: ['.jsx', '.tsx'],
|
||||||
|
xml: /** @type {string[]} */ (htmlSettingsDefault.xmlExtensions),
|
||||||
|
})
|
||||||
|
|
||||||
|
// This explicitly lists each entry so that we can get unused warnings
|
||||||
|
const fileGlobs = {
|
||||||
|
allScript: extArrayToGlobArray(fileExtensions.allScript),
|
||||||
|
html: extArrayToGlobArray(fileExtensions.html),
|
||||||
|
javaScript: extArrayToGlobArray(fileExtensions.javaScript),
|
||||||
|
markdown: extArrayToGlobArray(fileExtensions.markdown),
|
||||||
|
react: extArrayToGlobArray(fileExtensions.react),
|
||||||
|
typeScript: extArrayToGlobArray(fileExtensions.typeScript),
|
||||||
|
xml: extArrayToGlobArray(fileExtensions.xml),
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules for specific file types outside of the core JS/TS rule sets.
|
||||||
|
*/
|
||||||
|
const miscFileRules = tseslint.config([
|
||||||
|
// eslint-plugin-html
|
||||||
|
{
|
||||||
|
name: 'scratch/miscFileRules[eslint-plugin-html]',
|
||||||
|
files: [...fileGlobs.html, ...fileGlobs.xml],
|
||||||
|
plugins: { html },
|
||||||
|
settings: {
|
||||||
|
'html/html-extensions': fileExtensions.html,
|
||||||
|
'xml/xml-extensions': fileExtensions.xml,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// eslint-plugin-markdown
|
||||||
|
{
|
||||||
|
name: 'scratch/miscFileRules[eslint-plugin-markdown]',
|
||||||
|
files: fileGlobs.markdown,
|
||||||
|
extends: [markdown.configs.recommended],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules recommended for all script files, whether or not type information is available or checked.
|
||||||
|
*/
|
||||||
|
const allScriptRules = tseslint.config([
|
||||||
// eslint-plugin-formatjs
|
// eslint-plugin-formatjs
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[eslint-plugin-formatjs]',
|
||||||
plugins: {
|
plugins: {
|
||||||
formatjs,
|
formatjs,
|
||||||
},
|
},
|
||||||
|
@ -49,45 +98,24 @@ const typeFreeRules = tseslint.config(
|
||||||
'formatjs/no-offset': ['error'],
|
'formatjs/no-offset': ['error'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// eslint-plugin-html
|
|
||||||
{
|
|
||||||
files: htmlGlobs,
|
|
||||||
plugins: { html },
|
|
||||||
settings: {
|
|
||||||
'html/html-extensions': htmlExtensions,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// eslint-plugin-import
|
// eslint-plugin-import
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[eslint-plugin-import]',
|
||||||
plugins: importPlugin.flatConfigs.recommended.plugins,
|
plugins: importPlugin.flatConfigs.recommended.plugins,
|
||||||
rules: {
|
rules: {
|
||||||
'import/no-duplicates': 'error', // Forbid duplicate imports
|
'import/no-duplicates': 'error', // Forbid duplicate imports
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// eslint-plugin-jsdoc
|
|
||||||
jsdoc.configs['flat/recommended-error'],
|
|
||||||
{
|
|
||||||
files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
|
|
||||||
extends: [jsdoc.configs['flat/recommended-typescript-error']],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
// If JSDoc comments are present, they must be informative (non-trivial).
|
|
||||||
// For example, the description "The foo." on a variable called "foo" is not informative.
|
|
||||||
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
|
|
||||||
'jsdoc/informative-docs': ['error'],
|
|
||||||
|
|
||||||
// Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
|
|
||||||
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
|
|
||||||
'jsdoc/require-jsdoc': ['off'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// eslint-plugin-jsx-a11y
|
// eslint-plugin-jsx-a11y
|
||||||
jsxA11y.flatConfigs.recommended,
|
{
|
||||||
// eslint-plugin-markdown
|
name: 'scratch/allScriptRules[eslint-plugin-jsx-a11y]',
|
||||||
markdown.configs.recommended,
|
files: fileGlobs.react,
|
||||||
|
extends: [jsxA11y.flatConfigs.recommended],
|
||||||
|
},
|
||||||
// eslint-plugin-react
|
// eslint-plugin-react
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[eslint-plugin-react]',
|
||||||
|
files: fileGlobs.react,
|
||||||
plugins: {
|
plugins: {
|
||||||
react,
|
react,
|
||||||
},
|
},
|
||||||
|
@ -142,27 +170,17 @@ const typeFreeRules = tseslint.config(
|
||||||
},
|
},
|
||||||
// eslint-plugin-react-hooks
|
// eslint-plugin-react-hooks
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[eslint-plugin-react-hooks]',
|
||||||
|
files: fileGlobs.react,
|
||||||
extends: [reactHooks.configs['recommended-latest']],
|
extends: [reactHooks.configs['recommended-latest']],
|
||||||
rules: {
|
rules: {
|
||||||
// https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#advanced-configuration
|
// https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#advanced-configuration
|
||||||
'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useAsync$' }],
|
'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useAsync$' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// typescript-eslint
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
// https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
|
|
||||||
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': ['error'],
|
|
||||||
|
|
||||||
// https://typescript-eslint.io/rules/no-useless-constructor/
|
|
||||||
'@typescript-eslint/no-useless-constructor': ['error'],
|
|
||||||
|
|
||||||
// https://typescript-eslint.io/rules/no-non-null-assertion
|
|
||||||
'@typescript-eslint/no-non-null-assertion': ['error'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// @eslint-community/eslint-plugin-eslint-comments
|
// @eslint-community/eslint-plugin-eslint-comments
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[eslint-plugin-eslint-comments]',
|
||||||
extends: [
|
extends: [
|
||||||
// @ts-expect-error This plugin's recommended rules don't quite match the type `tseslint.config` expects.
|
// @ts-expect-error This plugin's recommended rules don't quite match the type `tseslint.config` expects.
|
||||||
eslintComments.recommended,
|
eslintComments.recommended,
|
||||||
|
@ -174,6 +192,7 @@ const typeFreeRules = tseslint.config(
|
||||||
},
|
},
|
||||||
// @eslint/js
|
// @eslint/js
|
||||||
{
|
{
|
||||||
|
name: 'scratch/allScriptRules[@eslint/js]',
|
||||||
rules: {
|
rules: {
|
||||||
// https://eslint.org/docs/latest/rules/arrow-body-style
|
// https://eslint.org/docs/latest/rules/arrow-body-style
|
||||||
'arrow-body-style': ['error', 'as-needed'],
|
'arrow-body-style': ['error', 'as-needed'],
|
||||||
|
@ -206,17 +225,48 @@ const typeFreeRules = tseslint.config(
|
||||||
'symbol-description': ['error'],
|
'symbol-description': ['error'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional rules recommended when information is available.
|
* Additional rules recommended when type information is not available or checked.
|
||||||
|
*/
|
||||||
|
const typeFreeRules = tseslint.config([
|
||||||
|
{
|
||||||
|
name: 'scratch/typeFreeRules[base]',
|
||||||
|
extends: [eslint.configs.recommended],
|
||||||
|
},
|
||||||
|
...allScriptRules,
|
||||||
|
{
|
||||||
|
name: 'scratch/typeFreeRules[eslint-plugin-jsdoc]',
|
||||||
|
extends: [jsdoc.configs['flat/recommended-error']],
|
||||||
|
rules: {
|
||||||
|
// If JSDoc comments are present, they must be informative (non-trivial).
|
||||||
|
// For example, the description "The foo." on a variable called "foo" is not informative.
|
||||||
|
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
|
||||||
|
'jsdoc/informative-docs': ['error'],
|
||||||
|
|
||||||
|
// Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
|
||||||
|
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
|
||||||
|
'jsdoc/require-jsdoc': ['off'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rules recommended when type information is available and checked. This configuration turns off some rules with the
|
||||||
|
* assumption that other software, such as TypeScript, will flag those problems. For example, the `no-undef` rule is
|
||||||
|
* disabled in this configuration. These rules include `allScriptRules`.
|
||||||
* These rules require additional configuration.
|
* These rules require additional configuration.
|
||||||
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
||||||
*/
|
*/
|
||||||
const typeCheckedRules = tseslint.config(
|
const typeCheckedRules = tseslint.config([
|
||||||
tseslint.configs.recommendedTypeChecked,
|
|
||||||
tseslint.configs.stylisticTypeChecked,
|
|
||||||
{
|
{
|
||||||
|
name: 'scratch/typeCheckedRules[base]',
|
||||||
|
extends: [
|
||||||
|
eslint.configs.recommended,
|
||||||
|
tseslint.configs.recommendedTypeChecked,
|
||||||
|
tseslint.configs.stylisticTypeChecked,
|
||||||
|
],
|
||||||
rules: {
|
rules: {
|
||||||
// https://typescript-eslint.io/rules/no-unnecessary-condition/
|
// https://typescript-eslint.io/rules/no-unnecessary-condition/
|
||||||
'@typescript-eslint/no-unnecessary-condition': ['error'],
|
'@typescript-eslint/no-unnecessary-condition': ['error'],
|
||||||
|
@ -225,7 +275,43 @@ const typeCheckedRules = tseslint.config(
|
||||||
'@typescript-eslint/require-await': ['error'],
|
'@typescript-eslint/require-await': ['error'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
...allScriptRules,
|
||||||
|
{
|
||||||
|
name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][1]',
|
||||||
|
extends: [jsdoc.configs['flat/recommended-error']],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][2]',
|
||||||
|
extends: [jsdoc.configs['flat/recommended-typescript-error']],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][3]',
|
||||||
|
rules: {
|
||||||
|
// If JSDoc comments are present, they must be informative (non-trivial).
|
||||||
|
// For example, the description "The foo." on a variable called "foo" is not informative.
|
||||||
|
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
|
||||||
|
'jsdoc/informative-docs': ['error'],
|
||||||
|
|
||||||
|
// Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
|
||||||
|
// https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
|
||||||
|
'jsdoc/require-jsdoc': ['off'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// typescript-eslint
|
||||||
|
{
|
||||||
|
name: 'scratch/typeCheckedRules[typescript-eslint]',
|
||||||
|
rules: {
|
||||||
|
// https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
|
||||||
|
'@typescript-eslint/no-non-null-asserted-nullish-coalescing': ['error'],
|
||||||
|
|
||||||
|
// https://typescript-eslint.io/rules/no-useless-constructor/
|
||||||
|
'@typescript-eslint/no-useless-constructor': ['error'],
|
||||||
|
|
||||||
|
// https://typescript-eslint.io/rules/no-non-null-assertion
|
||||||
|
'@typescript-eslint/no-non-null-assertion': ['error'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scratch's recommended configuration when type information is not available.
|
* Scratch's recommended configuration when type information is not available.
|
||||||
|
@ -235,9 +321,10 @@ const recommendedTypeFree = tseslint.config(typeFreeRules, eslintConfigPrettier)
|
||||||
/**
|
/**
|
||||||
* Scratch's recommended configuration when type information is available.
|
* Scratch's recommended configuration when type information is available.
|
||||||
* These rules require additional configuration.
|
* These rules require additional configuration.
|
||||||
|
* WARNING: These rules do not specify the `files` property.
|
||||||
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
||||||
*/
|
*/
|
||||||
const recommendedTypeChecked = tseslint.config(typeFreeRules, typeCheckedRules, eslintConfigPrettier)
|
const recommendedTypeChecked = tseslint.config(typeCheckedRules, eslintConfigPrettier)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scratch's recommended configuration for general use.
|
* Scratch's recommended configuration for general use.
|
||||||
|
@ -246,10 +333,22 @@ const recommendedTypeChecked = tseslint.config(typeFreeRules, typeCheckedRules,
|
||||||
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
* @see https://typescript-eslint.io/getting-started/typed-linting/
|
||||||
*/
|
*/
|
||||||
const recommended = tseslint.config(
|
const recommended = tseslint.config(
|
||||||
typeFreeRules,
|
|
||||||
{
|
{
|
||||||
files: typeScriptExtensions,
|
name: 'scratch/recommended',
|
||||||
|
},
|
||||||
|
miscFileRules,
|
||||||
|
{
|
||||||
|
files: fileGlobs.allScript,
|
||||||
|
extends: [typeFreeRules],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: fileGlobs.typeScript,
|
||||||
extends: [typeCheckedRules],
|
extends: [typeCheckedRules],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
eslintConfigPrettier,
|
eslintConfigPrettier,
|
||||||
)
|
)
|
||||||
|
@ -258,4 +357,4 @@ const recommended = tseslint.config(
|
||||||
export { config } from 'typescript-eslint'
|
export { config } from 'typescript-eslint'
|
||||||
|
|
||||||
// Our exported configurations
|
// Our exported configurations
|
||||||
export { recommended, recommendedTypeChecked, recommendedTypeFree, legacy }
|
export { recommended, recommendedTypeChecked, recommendedTypeFree, miscFileRules, legacy }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue