diff --git a/.eslintrc.format.js b/.eslintrc.format.js deleted file mode 100644 index 1b2467b..0000000 --- a/.eslintrc.format.js +++ /dev/null @@ -1,12 +0,0 @@ -require('@rushstack/eslint-patch/modern-module-resolution'); - -module.exports = { - root: true, - plugins: ['@cloudflight/typescript'], - extends: ['plugin:@cloudflight/typescript/formatting'], - ignorePatterns: ['*.json'], - env: { - es6: true, - node: true, - }, -}; diff --git a/eslint.config.mjs b/eslint.config.mjs index baefbb5..56ad28b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,8 +1,8 @@ -import tseslint from 'typescript-eslint'; +import {cloudflightTypescriptConfig} from '@cloudflight/eslint-plugin-typescript'; import {includeIgnoreFile} from '@eslint/compat'; import {dirname, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; -import { cloudflightTypescriptConfig } from '@cloudflight/eslint-plugin-typescript'; +import tseslint from 'typescript-eslint'; const filename = fileURLToPath(import.meta.url); const directory = dirname(filename); @@ -18,16 +18,15 @@ export default tseslint.config( 'packages/eslint-plugin-typescript/src/configs/index.ts', 'packages/eslint-plugin-typescript/src/configs/json.ts', 'packages/eslint-plugin-typescript/src/configs/package.ts', - 'packages/eslint-plugin-typescript/src/configs/format.ts', 'packages/eslint-plugin-typescript/src/configs/rxjs.ts', 'packages/eslint-plugin-typescript/src/rules/package/*', 'packages/eslint-plugin-typescript/src/rules/ts-config/*', 'packages/eslint-plugin-typescript/src/rules/typescript/*.spec.ts', 'scripts/*', - '.eslintrc.format.js', 'eslint.config.mjs', + 'eslint.format.mjs', 'jest.config*.ts', - ] + ], }, ...cloudflightTypescriptConfig, { diff --git a/eslint.format.mjs b/eslint.format.mjs new file mode 100644 index 0000000..a96726b --- /dev/null +++ b/eslint.format.mjs @@ -0,0 +1,14 @@ +import {cloudflightTypescriptFormatConfig} from '@cloudflight/eslint-plugin-typescript'; +import {includeIgnoreFile} from '@eslint/compat'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; +import tseslint from 'typescript-eslint'; + +const filename = fileURLToPath(import.meta.url); +const directory = dirname(filename); +const gitignorePath = resolve(directory, '.gitignore'); + +export default tseslint.config( + includeIgnoreFile(gitignorePath), + ...cloudflightTypescriptFormatConfig, +); diff --git a/package.json b/package.json index 2546ed4..2462d01 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "private": true, "volta": { "node": "20.12.2", @@ -16,7 +16,7 @@ "description": "", "scripts": { "build": "yarn workspaces foreach -pA --topological-dev run build-package", - "lint": "eslint .", + "lint": "eslint . && eslint -c eslint.format.mjs .", "test": "yarn workspaces foreach -pA run test-package", "postinstall": "husky", "publish-workspaces": "node ./scripts/prepare.js && yarn workspaces foreach -A --no-private npm publish" @@ -30,6 +30,7 @@ "devDependencies": { "@eslint/compat": "1.2.0", "@eslint/js": "9.11.1", + "@stylistic/eslint-plugin": "2.9.0", "@types/eslint": "9.6.1", "@types/eslint__js": "8.42.3", "@types/estree": "1.0.6", @@ -47,6 +48,7 @@ "eslint-plugin-jsx-a11y": "6.10.0", "eslint-plugin-n": "17.10.3", "eslint-plugin-no-unsanitized": "4.1.2", + "eslint-plugin-perfectionist": "3.9.0", "eslint-plugin-react": "7.37.1", "eslint-plugin-react-hooks": "5.0.0", "eslint-plugin-rxjs": "5.0.3", @@ -61,7 +63,7 @@ "typescript-eslint": "8.8.0" }, "lint-staged": { - "*.js": "echo 'Not Implemented'", - "*.ts": "echo 'Not Implemented'" + "*.js": "eslint -c eslint.format.mjs --fix", + "*.ts": "eslint -c eslint.format.mjs --fix" } } diff --git a/packages/eslint-plugin-angular/package.json b/packages/eslint-plugin-angular/package.json index 628bd8d..9c77281 100644 --- a/packages/eslint-plugin-angular/package.json +++ b/packages/eslint-plugin-angular/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin-angular", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "description": "Cloudflight eslint-plugin & eslint-config for angular", "volta": { "extends": "../../package.json" diff --git a/packages/eslint-plugin-angular/src/index.ts b/packages/eslint-plugin-angular/src/index.ts index 0a1764b..ef47964 100644 --- a/packages/eslint-plugin-angular/src/index.ts +++ b/packages/eslint-plugin-angular/src/index.ts @@ -1,10 +1,11 @@ -import tseslint from 'typescript-eslint'; -import angular from 'angular-eslint'; import {cloudflightTypescriptConfig} from '@cloudflight/eslint-plugin-typescript'; -import {eslintRules} from './configs/eslint'; -import {typescriptEslintRules} from './configs/typescript-eslint'; +import angular from 'angular-eslint'; +import tseslint from 'typescript-eslint'; + import {angularEslintRules} from './configs/angular-eslint'; import {angularTemplateEslintRules} from './configs/angular-eslint-template'; +import {eslintRules} from './configs/eslint'; +import {typescriptEslintRules} from './configs/typescript-eslint'; export const cloudflightAngularTypescriptConfig = tseslint.config( ...cloudflightTypescriptConfig, @@ -19,7 +20,7 @@ export const cloudflightAngularTypescriptConfig = tseslint.config( ...eslintRules, ...typescriptEslintRules, ...angularEslintRules, - } + }, }, ); @@ -35,8 +36,8 @@ export const cloudflightAngularTemplateConfig = tseslint.config( ...angularTemplateEslintRules, // todo: this should be its own config // ...formatAngularTemplateEslintRules, - } - } + }, + }, ); export const cloudflightAngularConfig = tseslint.config( diff --git a/packages/eslint-plugin-node/package.json b/packages/eslint-plugin-node/package.json index b06b7f3..e9a938e 100644 --- a/packages/eslint-plugin-node/package.json +++ b/packages/eslint-plugin-node/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin-node", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "description": "Cloudflight eslint-plugin & eslint-config for node", "volta": { "extends": "../../package.json" diff --git a/packages/eslint-plugin-node/src/configs/security.ts b/packages/eslint-plugin-node/src/configs/security.ts index 042b04f..8fb7e0a 100644 --- a/packages/eslint-plugin-node/src/configs/security.ts +++ b/packages/eslint-plugin-node/src/configs/security.ts @@ -19,7 +19,8 @@ function warningEntryToError(value: TSESLint.Linter.RuleEntry): TSESLint.Linter. function warningLevelToError(level: TSESLint.Linter.RuleLevel): TSESLint.Linter.RuleLevel { if (level === 'warn') { return 'error'; - } else if (level === 1) { + } + else if (level === 1) { return 'error'; } diff --git a/packages/eslint-plugin-node/src/index.ts b/packages/eslint-plugin-node/src/index.ts index 9843d29..17d6956 100644 --- a/packages/eslint-plugin-node/src/index.ts +++ b/packages/eslint-plugin-node/src/index.ts @@ -1,11 +1,12 @@ -import tseslint from 'typescript-eslint'; -import { cloudflightTypescriptConfig } from '@cloudflight/eslint-plugin-typescript'; +import {cloudflightTypescriptConfig} from '@cloudflight/eslint-plugin-typescript'; +import pluginNode from 'eslint-plugin-n'; import nounsanitized from 'eslint-plugin-no-unsanitized'; import pluginSecurity from 'eslint-plugin-security'; -import pluginNode from 'eslint-plugin-n'; -import {securityRules} from './configs/security'; -import {nodeRules} from './configs/node'; +import tseslint from 'typescript-eslint'; + import {importEslintRules} from './configs/import'; +import {nodeRules} from './configs/node'; +import {securityRules} from './configs/security'; export const cloudflightNodeConfig = tseslint.config( ...cloudflightTypescriptConfig, @@ -21,6 +22,6 @@ export const cloudflightNodeConfig = tseslint.config( ...nodeRules, ...securityRules, ...importEslintRules, - } + }, }, ); diff --git a/packages/eslint-plugin-react/package.json b/packages/eslint-plugin-react/package.json index 12181f5..cd2aa0e 100644 --- a/packages/eslint-plugin-react/package.json +++ b/packages/eslint-plugin-react/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin-react", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "description": "Cloudflight eslint-plugin & eslint-config for React", "volta": { "extends": "../../package.json" diff --git a/packages/eslint-plugin-react/src/index.ts b/packages/eslint-plugin-react/src/index.ts index 567375a..d27c8fd 100644 --- a/packages/eslint-plugin-react/src/index.ts +++ b/packages/eslint-plugin-react/src/index.ts @@ -1,16 +1,18 @@ -import tseslint from 'typescript-eslint'; +import type {TSESLint} from '@typescript-eslint/utils'; + import { cloudflightTypescriptBaseConfig, cloudflightTypescriptConfig, cloudflightTypescriptDisableTypeCheckedConfig, - cloudflightTypescriptImportConfig + cloudflightTypescriptImportConfig, } from '@cloudflight/eslint-plugin-typescript'; -import type {TSESLint} from '@typescript-eslint/utils'; import * as tsParser from '@typescript-eslint/parser'; +import jsxA11y from 'eslint-plugin-jsx-a11y'; +import tseslint from 'typescript-eslint'; // see https://github.com/jsx-eslint/eslint-plugin-react/issues/3838 // eslint-disable-next-line import-x/default import react from 'eslint-plugin-react'; import pluginReactHooks from 'eslint-plugin-react-hooks'; -import jsxA11y from 'eslint-plugin-jsx-a11y'; + import {reactRules} from './configs/react'; export const cloudflightReactConfig = tseslint.config( @@ -51,7 +53,7 @@ export const cloudflightReactConfig = tseslint.config( rules: { ...pluginReactHooks.configs.recommended.rules, ...reactRules, - } + }, }, { files: ['**/*.{js,jsx,mjs,cjs}'], diff --git a/packages/eslint-plugin-typescript/README.md b/packages/eslint-plugin-typescript/README.md index 2670135..8fab502 100644 --- a/packages/eslint-plugin-typescript/README.md +++ b/packages/eslint-plugin-typescript/README.md @@ -73,24 +73,26 @@ In your `package.json` add the following: } ``` -Create a new file called `.eslintrc.format.js` and add the following: +Create a new file called `eslint.format.mjs` and add the following: -``` -require('@rushstack/eslint-patch/modern-module-resolution'); - -module.exports = { - root: true, - plugins: ['@cloudflight/typescript'], - extends: ['plugin:@cloudflight/typescript/formatting'], - ignorePatterns: ['jest.config*.ts'], - env: { - es6: true, - node: true, - }, -}; +```ts +import tseslint from 'typescript-eslint'; +import {includeIgnoreFile} from '@eslint/compat'; +import {dirname, resolve} from 'node:path'; +import {fileURLToPath} from 'node:url'; +import {cloudflightTypescriptFormatConfig} from '@cloudflight/eslint-plugin-typescript'; + +const filename = fileURLToPath(import.meta.url); +const directory = dirname(filename); +const gitignorePath = resolve(directory, '.gitignore'); + +export default tseslint.config( + includeIgnoreFile(gitignorePath), + ...cloudflightTypescriptFormatConfig, +); ``` -With the command `eslint . --config .eslintrc.format.js` your project can be checked for formatting violations. +With the command `eslint -c eslint.format.mjs .` your project can be checked for formatting violations. ### Pre-Commit Hook @@ -102,7 +104,7 @@ To automatically format your code before committing, set up [husky](https://typi { // ... "lint-staged": { - "*.ts": "eslint --config .eslintrc.format.cjs --fix" + "*.ts": "eslint -c eslint.format.mjs --fix" } } ``` diff --git a/packages/eslint-plugin-typescript/jest.config.ts b/packages/eslint-plugin-typescript/jest.config.ts index c4eb730..62a1f68 100644 --- a/packages/eslint-plugin-typescript/jest.config.ts +++ b/packages/eslint-plugin-typescript/jest.config.ts @@ -1,4 +1,5 @@ import type {Config} from 'jest'; + import {baseJestConfig} from '../../jest.config.base'; const jestConfig: Config = { diff --git a/packages/eslint-plugin-typescript/package.json b/packages/eslint-plugin-typescript/package.json index 2bcf3ad..d6ffe23 100644 --- a/packages/eslint-plugin-typescript/package.json +++ b/packages/eslint-plugin-typescript/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin-typescript", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "description": "Cloudflight eslint-plugin & eslint-config for typescript", "volta": { "extends": "../../package.json" diff --git a/packages/eslint-plugin-typescript/src/configs/custom.ts b/packages/eslint-plugin-typescript/src/configs/custom.ts index 7ffe7d4..ddd9060 100644 --- a/packages/eslint-plugin-typescript/src/configs/custom.ts +++ b/packages/eslint-plugin-typescript/src/configs/custom.ts @@ -1,4 +1,4 @@ -import {TSESLint} from '@typescript-eslint/utils'; +import type {TSESLint} from '@typescript-eslint/utils'; import {NoMomentJsRuleName} from '../rules/typescript/no-moment-js'; import {NoOnEventAssignName} from '../rules/typescript/no-on-event-assign'; diff --git a/packages/eslint-plugin-typescript/src/configs/eslint.ts b/packages/eslint-plugin-typescript/src/configs/eslint.ts index 9c85f1e..3c2b48b 100644 --- a/packages/eslint-plugin-typescript/src/configs/eslint.ts +++ b/packages/eslint-plugin-typescript/src/configs/eslint.ts @@ -1,4 +1,4 @@ -import {TSESLint} from '@typescript-eslint/utils'; +import type {TSESLint} from '@typescript-eslint/utils'; export const eslintRules: TSESLint.Linter.RulesRecord = { complexity: ['error', {max: 20}], diff --git a/packages/eslint-plugin-typescript/src/configs/format.ts b/packages/eslint-plugin-typescript/src/configs/format.ts index bf9d63b..89fbc81 100644 --- a/packages/eslint-plugin-typescript/src/configs/format.ts +++ b/packages/eslint-plugin-typescript/src/configs/format.ts @@ -1,7 +1,9 @@ -import {TSESLint} from '@typescript-eslint/utils'; +import type {TSESLint} from '@typescript-eslint/utils'; + +const pluginPrefix = '@stylistic'; export const formatEslintRules: TSESLint.Linter.RulesRecord = { - 'max-len': [ + [`${pluginPrefix}/max-len`]: [ 'error', { ignorePattern: '^(import|export) [^,]+ from', @@ -11,24 +13,24 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { code: 280, }, ], - 'eol-last': ['error', 'always'], - 'no-multi-spaces': [ + [`${pluginPrefix}/eol-last`]: ['error', 'always'], + [`${pluginPrefix}/no-multi-spaces`]: [ 'error', { ignoreEOLComments: false, }, ], - 'dot-location': ['error', 'property'], - 'template-tag-spacing': ['error', 'never'], - 'template-curly-spacing': ['error', 'never'], - 'switch-colon-spacing': [ + [`${pluginPrefix}/dot-location`]: ['error', 'property'], + [`${pluginPrefix}/template-tag-spacing`]: ['error', 'never'], + [`${pluginPrefix}/template-curly-spacing`]: ['error', 'never'], + [`${pluginPrefix}/switch-colon-spacing`]: [ 'error', { after: true, before: false, }, ], - 'spaced-comment': [ + [`${pluginPrefix}/spaced-comment`]: [ 'error', 'always', { @@ -58,8 +60,8 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { }, }, ], - 'space-infix-ops': ['error'], - 'space-unary-ops': [ + [`${pluginPrefix}/space-infix-ops`]: ['error'], + [`${pluginPrefix}/space-unary-ops`]: [ 'error', { words: true, @@ -67,9 +69,9 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { overrides: {}, }, ], - 'space-in-parens': ['error', 'never'], - 'space-before-blocks': ['error'], - 'space-before-function-paren': [ + [`${pluginPrefix}/space-in-parens`]: ['error', 'never'], + [`${pluginPrefix}/space-before-blocks`]: ['error'], + [`${pluginPrefix}/space-before-function-paren`]: [ 'error', { anonymous: 'always', @@ -77,14 +79,14 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { asyncArrow: 'always', }, ], - 'semi-spacing': [ + [`${pluginPrefix}/semi-spacing`]: [ 'error', { before: false, after: true, }, ], - 'padded-blocks': [ + [`${pluginPrefix}/padded-blocks`]: [ 'error', { blocks: 'never', @@ -95,38 +97,37 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { allowSingleLineBlocks: true, }, ], - 'padding-line-between-statements': [ + [`${pluginPrefix}/padding-line-between-statements`]: [ 'error', {blankLine: 'always', prev: '*', next: 'return'}, {blankLine: 'always', prev: 'const', next: '*'}, {blankLine: 'any', prev: 'const', next: ['const', 'let', 'var']}, {blankLine: 'always', prev: 'block', next: '*'}, ], - 'brace-style': [ + [`${pluginPrefix}/brace-style`]: [ 'error', 'stroustrup', { allowSingleLine: false, }, ], - 'no-spaced-func': ['error'], - 'no-trailing-spaces': [ + [`${pluginPrefix}/no-trailing-spaces`]: [ 'error', { skipBlankLines: false, ignoreComments: false, }, ], - 'no-whitespace-before-property': ['error'], - 'object-property-newline': [ + [`${pluginPrefix}/no-whitespace-before-property`]: ['error'], + [`${pluginPrefix}/object-property-newline`]: [ 'error', { allowAllPropertiesOnSameLine: true, allowMultiplePropertiesPerLine: false, }, ], - 'no-mixed-spaces-and-tabs': ['error'], - 'no-multiple-empty-lines': [ + [`${pluginPrefix}/no-mixed-spaces-and-tabs`]: ['error'], + [`${pluginPrefix}/no-multiple-empty-lines`]: [ 'error', { max: 1, @@ -134,14 +135,14 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { maxEOF: 0, }, ], - 'key-spacing': [ + [`${pluginPrefix}/key-spacing`]: [ 'error', { beforeColon: false, afterColon: true, }, ], - 'keyword-spacing': [ + [`${pluginPrefix}/keyword-spacing`]: [ 'error', { before: true, @@ -160,7 +161,7 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { }, ], // this rule is disabled, but configured in case anyone enables this rule. - 'line-comment-position': [ + [`${pluginPrefix}/line-comment-position`]: [ 'off', { position: 'above', @@ -168,30 +169,23 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { applyDefaultPatterns: true, }, ], - 'lines-between-class-members': [ + [`${pluginPrefix}/lines-between-class-members`]: [ 'error', 'always', { exceptAfterSingleLine: false, }, ], - 'lines-around-directive': [ - 'error', - { - before: 'always', - after: 'always', - }, - ], - 'newline-per-chained-call': [ + [`${pluginPrefix}/newline-per-chained-call`]: [ 'error', { ignoreChainWithDepth: 4, }, ], - 'function-call-argument-newline': ['error', 'consistent'], - 'func-call-spacing': ['error', 'never'], - 'function-paren-newline': ['error', 'multiline-arguments'], - 'comma-dangle': [ + [`${pluginPrefix}/function-call-argument-newline`]: ['error', 'consistent'], + [`${pluginPrefix}/func-call-spacing`]: ['error', 'never'], + [`${pluginPrefix}/function-paren-newline`]: ['error', 'multiline-arguments'], + [`${pluginPrefix}/comma-dangle`]: [ 'error', { arrays: 'always-multiline', @@ -201,7 +195,7 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { functions: 'always-multiline', }, ], - 'comma-style': [ + [`${pluginPrefix}/comma-style`]: [ 'error', 'last', { @@ -220,25 +214,25 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { }, }, ], - 'comma-spacing': [ + [`${pluginPrefix}/comma-spacing`]: [ 'error', { before: false, after: true, }, ], - 'computed-property-spacing': ['error', 'never'], - 'generator-star-spacing': [ + [`${pluginPrefix}/computed-property-spacing`]: ['error', 'never'], + [`${pluginPrefix}/generator-star-spacing`]: [ 'error', { before: false, after: true, }, ], - 'rest-spread-spacing': ['error', 'never'], - 'array-bracket-newline': ['error', 'consistent'], - 'array-bracket-spacing': ['error', 'never'], - 'array-element-newline': [ + [`${pluginPrefix}/rest-spread-spacing`]: ['error', 'never'], + [`${pluginPrefix}/array-bracket-newline`]: ['error', 'consistent'], + [`${pluginPrefix}/array-bracket-spacing`]: ['error', 'never'], + [`${pluginPrefix}/array-element-newline`]: [ 'error', { ArrayExpression: 'consistent', @@ -247,29 +241,19 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { }, }, ], - 'block-spacing': ['error', 'always'], - 'arrow-parens': ['error', 'always'], - 'arrow-body-style': ['error', 'as-needed'], - 'arrow-spacing': [ + [`${pluginPrefix}/block-spacing`]: ['error', 'always'], + [`${pluginPrefix}/arrow-parens`]: ['error', 'always'], + [`${pluginPrefix}/arrow-spacing`]: [ 'error', { before: true, after: true, }, ], - 'implicit-arrow-linebreak': ['error', 'beside'], - 'dot-notation': 'off', - '@typescript-eslint/dot-notation': [ + [`${pluginPrefix}/implicit-arrow-linebreak`]: ['error', 'beside'], + [`${pluginPrefix}/indent`]: [ 'error', - { - allowKeywords: true, - allowPattern: '', - allowIndexSignaturePropertyAccess: true, - }, - ], - indent: [ - 'error', - // eslint-disable-next-line no-magic-numbers -- Indent of 4 spaces. + // eslint-disable-next-line no-magic-numbers 4, { SwitchCase: 1, @@ -307,30 +291,16 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild', - ], - ignoreComments: false, - offsetTernaryExpressions: false, - }, - ], - '@typescript-eslint/indent': [ - 'error', - // eslint-disable-next-line no-magic-numbers -- Indent of 4 spaces. - 4, - { - MemberExpression: 1, - SwitchCase: 1, - ignoredNodes: [ 'FunctionExpression > .params[decorators.length > 0]', 'FunctionExpression > .params > :matches(Decorator, :not(:first-child))', 'ClassBody.body > PropertyDefinition[decorators.length > 0] > .key', ], - flatTernaryExpressions: false, - offsetTernaryExpressions: false, ignoreComments: false, + offsetTernaryExpressions: false, + MemberExpression: 1, }, ], - quotes: 'off', - '@typescript-eslint/quotes': [ + [`${pluginPrefix}/quotes`]: [ 'error', 'single', { @@ -338,36 +308,50 @@ export const formatEslintRules: TSESLint.Linter.RulesRecord = { allowTemplateLiterals: true, }, ], - 'jsx-quotes': ['error', 'prefer-double'], - 'linebreak-style': ['off', 'unix'], - 'object-curly-spacing': ['error', 'never'], - 'object-curly-newline': [ + [`${pluginPrefix}/jsx-quotes`]: ['error', 'prefer-double'], + [`${pluginPrefix}/linebreak-style`]: ['off', 'unix'], + [`${pluginPrefix}/object-curly-spacing`]: ['error', 'never'], + [`${pluginPrefix}/object-curly-newline`]: [ 'error', { consistent: true, }, ], - 'operator-linebreak': ['error', 'after'], - 'sort-imports': 'off', - 'simple-import-sort/imports': [ + [`${pluginPrefix}/operator-linebreak`]: ['error', 'after'], + 'perfectionist/sort-imports': [ 'error', { - groups: [ - // Packages. `react` related packages come first. - ['^react$', '^@nest', '^@?\\w'], - // Internal packages. - ['@common', '^(@|@company|@ui|components|utils|config|vendored-lib)(/.*|$)'], - // Side effect imports. - ['^\\u0000'], - // Parent imports. Put `..` last. - ['^\\.\\.(?!/?$)', '^\\.\\./?$'], - // Other relative imports. Put same-folder imports and `.` last. - ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], - // Style imports. - ['^.+\\.s?css$'], - ], + type: 'natural', + order: 'asc', }, ], - 'simple-import-sort/exports': 'error', - 'import/newline-after-import': 'error', + 'perfectionist/sort-exports': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-named-imports': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'perfectionist/sort-named-exports': [ + 'error', + { + type: 'natural', + order: 'asc', + }, + ], + 'import-x/newline-after-import': 'error', + [`${pluginPrefix}/member-delimiter-style`]: [ + 'error', + {multiline: {delimiter: 'semi', requireLast: true}, singleline: {delimiter: 'semi', requireLast: false}}, + ], + [`${pluginPrefix}/semi`]: ['error'], + [`${pluginPrefix}/no-extra-semi`]: ['error'], + [`${pluginPrefix}/type-annotation-spacing`]: ['error'], }; diff --git a/packages/eslint-plugin-typescript/src/configs/import.ts b/packages/eslint-plugin-typescript/src/configs/import.ts index 714555a..b2912fa 100644 --- a/packages/eslint-plugin-typescript/src/configs/import.ts +++ b/packages/eslint-plugin-typescript/src/configs/import.ts @@ -1,4 +1,4 @@ -import {TSESLint} from '@typescript-eslint/utils'; +import type {TSESLint} from '@typescript-eslint/utils'; const pluginPrefix = 'import-x'; diff --git a/packages/eslint-plugin-typescript/src/configs/typescript-eslint.ts b/packages/eslint-plugin-typescript/src/configs/typescript-eslint.ts index 33cd9e5..c38f5bc 100644 --- a/packages/eslint-plugin-typescript/src/configs/typescript-eslint.ts +++ b/packages/eslint-plugin-typescript/src/configs/typescript-eslint.ts @@ -1,4 +1,4 @@ -import {TSESLint} from '@typescript-eslint/utils'; +import type {TSESLint} from '@typescript-eslint/utils'; const pluginPrefix = '@typescript-eslint'; diff --git a/packages/eslint-plugin-typescript/src/index.ts b/packages/eslint-plugin-typescript/src/index.ts index 5611dd2..6d723b3 100644 --- a/packages/eslint-plugin-typescript/src/index.ts +++ b/packages/eslint-plugin-typescript/src/index.ts @@ -1,12 +1,16 @@ import pluginJs from '@eslint/js'; -import tseslint from 'typescript-eslint'; -import eslintPluginImportX from 'eslint-plugin-import-x'; +import stylistic from '@stylistic/eslint-plugin'; import * as tsParser from '@typescript-eslint/parser'; +import eslintPluginImportX from 'eslint-plugin-import-x'; +import perfectionist from 'eslint-plugin-perfectionist'; +import tseslint from 'typescript-eslint'; + +import {customRules} from './configs/custom'; import {eslintRules} from './configs/eslint'; +import {formatEslintRules} from './configs/format'; +import {importEslintRules} from './configs/import'; import {typescriptEslintRules, typescriptEslintRulesDisableTypeChecked} from './configs/typescript-eslint'; -import {customRules} from './configs/custom'; import {cloudflightTypescriptPlugin} from './rules'; -import {importEslintRules} from './configs/import'; /** * @deprecated Use `cloudflightTypescriptConfig` instead @@ -74,3 +78,26 @@ export const cloudflightTypescriptConfig = tseslint.config( // eslint-disable-next-line @typescript-eslint/no-deprecated ...cloudflightTypescriptDisableTypeCheckedConfig, ); + +export const cloudflightTypescriptFormatConfig = tseslint.config({ + files: ['**/*.{js,mjs,cjs,ts,mts,cts}'], + plugins: { + '@typescript-eslint': tseslint.plugin, + 'import-x': eslintPluginImportX, + '@cloudflight/typescript': cloudflightTypescriptPlugin, + '@stylistic': stylistic, + perfectionist, + }, + languageOptions: { + parser: tsParser, + ecmaVersion: 'latest', + sourceType: 'module', + }, + linterOptions: { + reportUnusedDisableDirectives: 'off', + }, + name: 'cloudflight/typescript/format-rules', + rules: { + ...formatEslintRules, + }, +}); diff --git a/packages/eslint-plugin-typescript/src/rules/index.ts b/packages/eslint-plugin-typescript/src/rules/index.ts index c5af90d..07fe1c9 100644 --- a/packages/eslint-plugin-typescript/src/rules/index.ts +++ b/packages/eslint-plugin-typescript/src/rules/index.ts @@ -1,4 +1,5 @@ import {TSESLint} from '@typescript-eslint/utils'; + import {NoMomentJsRule, NoMomentJsRuleName} from './typescript/no-moment-js'; import {NoOnEventAssign, NoOnEventAssignName} from './typescript/no-on-event-assign'; @@ -31,4 +32,4 @@ export const cloudflightTypescriptPlugin: TSESLint.FlatConfig.Plugin = { version: 'see package.json file', }, rules, -} +}; diff --git a/packages/eslint-plugin-vue/package.json b/packages/eslint-plugin-vue/package.json index c649467..dcddcc9 100644 --- a/packages/eslint-plugin-vue/package.json +++ b/packages/eslint-plugin-vue/package.json @@ -1,6 +1,6 @@ { "name": "@cloudflight/eslint-plugin-vue", - "version": "1.0.0-rc9", + "version": "1.0.0-rc10", "description": "Cloudflight eslint-plugin & eslint-config for vue", "volta": { "extends": "../../package.json" diff --git a/packages/eslint-plugin-vue/src/configs/vue.ts b/packages/eslint-plugin-vue/src/configs/vue.ts index 4759d24..f25b115 100644 --- a/packages/eslint-plugin-vue/src/configs/vue.ts +++ b/packages/eslint-plugin-vue/src/configs/vue.ts @@ -38,7 +38,6 @@ export const vueRules: TSESLint.Linter.RulesRecord = { }, ], 'vue/html-button-has-type': 'error', - 'vue/no-ref-object-destructure': 'error', 'vue/no-undef-properties': 'error', // disable stylistic rules 'vue/html-indent': 'off', @@ -59,7 +58,8 @@ function warningEntryToError(value: TSESLint.Linter.RuleEntry): TSESLint.Linter. function warningLevelToError(level: TSESLint.Linter.RuleLevel): TSESLint.Linter.RuleLevel { if (level === 'warn') { return 'error'; - } else if (level === 1) { + } + else if (level === 1) { return 'error'; } diff --git a/packages/eslint-plugin-vue/src/index.ts b/packages/eslint-plugin-vue/src/index.ts index dea13f3..4ddae6b 100644 --- a/packages/eslint-plugin-vue/src/index.ts +++ b/packages/eslint-plugin-vue/src/index.ts @@ -1,10 +1,11 @@ -import tseslint from 'typescript-eslint'; -import { cloudflightTypescriptBaseConfig, cloudflightTypescriptConfig } from '@cloudflight/eslint-plugin-typescript'; +import {cloudflightTypescriptBaseConfig, cloudflightTypescriptConfig} from '@cloudflight/eslint-plugin-typescript'; import {TSESLint} from '@typescript-eslint/utils'; +import vueTsEslintConfig from '@vue/eslint-config-typescript'; +import tseslint from 'typescript-eslint'; // @ts-expect-error .d.ts file for eslint-plugin-vue has been implemented but not released yet // if the following line fails to build after updating the version, simply remove this comment import pluginVue from 'eslint-plugin-vue'; -import vueTsEslintConfig from '@vue/eslint-config-typescript'; + import {typescriptRules} from './configs/typescript'; import {vueRules} from './configs/vue'; @@ -27,6 +28,6 @@ export const cloudflightVueConfig = tseslint.config( rules: { ...typescriptRules, ...vueRules, - } + }, }, ); diff --git a/packages/types-eslint-plugin-jsx-a11y/index.d.ts b/packages/types-eslint-plugin-jsx-a11y/index.d.ts index 76f2538..2034012 100644 --- a/packages/types-eslint-plugin-jsx-a11y/index.d.ts +++ b/packages/types-eslint-plugin-jsx-a11y/index.d.ts @@ -1,9 +1,13 @@ -import type { Linter } from 'eslint'; +import type {Linter} from 'eslint'; declare const jsxA11y: { readonly flatConfigs: { - readonly recommended: { readonly rules: Readonly }; - readonly strict: { readonly rules: Readonly }; + readonly recommended: { + readonly rules: Readonly; + }; + readonly strict: { + readonly rules: Readonly; + }; }; }; diff --git a/packages/types-eslint-plugin-no-unsanitized/index.d.ts b/packages/types-eslint-plugin-no-unsanitized/index.d.ts index 9b587d4..2d513c4 100644 --- a/packages/types-eslint-plugin-no-unsanitized/index.d.ts +++ b/packages/types-eslint-plugin-no-unsanitized/index.d.ts @@ -1,8 +1,10 @@ -import type { Linter } from 'eslint'; +import type {Linter} from 'eslint'; declare const nounsanitized: { readonly configs: { - readonly recommended: { readonly rules: Readonly }; + readonly recommended: { + readonly rules: Readonly; + }; }; }; diff --git a/packages/types-eslint-plugin-react-hooks/index.d.ts b/packages/types-eslint-plugin-react-hooks/index.d.ts index fcc9a81..b5fe8c5 100644 --- a/packages/types-eslint-plugin-react-hooks/index.d.ts +++ b/packages/types-eslint-plugin-react-hooks/index.d.ts @@ -1,8 +1,10 @@ -import type { Linter } from 'eslint'; +import type {Linter} from 'eslint'; declare const pluginReactHooks: { readonly configs: { - readonly recommended: { readonly rules: Readonly }; + readonly recommended: { + readonly rules: Readonly; + }; }; }; diff --git a/packages/types-eslint-plugin-security/index.d.ts b/packages/types-eslint-plugin-security/index.d.ts index 0314d35..e79ad39 100644 --- a/packages/types-eslint-plugin-security/index.d.ts +++ b/packages/types-eslint-plugin-security/index.d.ts @@ -1,8 +1,10 @@ -import type { Linter } from 'eslint'; +import type {Linter} from 'eslint'; declare const pluginSecurity: { readonly configs: { - readonly recommended: { readonly rules: Readonly }; + readonly recommended: { + readonly rules: Readonly; + }; }; }; diff --git a/scripts/copy-license/copy-license.js b/scripts/copy-license/copy-license.js index 4622945..2c21344 100644 --- a/scripts/copy-license/copy-license.js +++ b/scripts/copy-license/copy-license.js @@ -8,17 +8,21 @@ const rootLicenseFile = path.join(rootDir, licenseFileName); function isNpmPackage(path) { const files = fs.readdirSync(path); + return files.some((fileName) => fileName === 'package.json'); } module.exports = { copyLicenses() { const packages = fs.readdirSync(packagesDir); + for (const packageDirName of packages) { const packagePath = path.join(packagesDir, packageDirName); const packageStat = fs.statSync(packagePath); + if (packageStat.isDirectory() && isNpmPackage(packagePath)) { const packageLicenseFile = path.join(packagePath, licenseFileName); + fs.copyFileSync(rootLicenseFile, packageLicenseFile); } } diff --git a/yarn.lock b/yarn.lock index e16b528..3114a8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -586,6 +586,7 @@ __metadata: dependencies: "@eslint/compat": "npm:1.2.0" "@eslint/js": "npm:9.11.1" + "@stylistic/eslint-plugin": "npm:2.9.0" "@types/eslint": "npm:9.6.1" "@types/eslint__js": "npm:8.42.3" "@types/estree": "npm:1.0.6" @@ -603,6 +604,7 @@ __metadata: eslint-plugin-jsx-a11y: "npm:6.10.0" eslint-plugin-n: "npm:17.10.3" eslint-plugin-no-unsanitized: "npm:4.1.2" + eslint-plugin-perfectionist: "npm:3.9.0" eslint-plugin-react: "npm:7.37.1" eslint-plugin-react-hooks: "npm:5.0.0" eslint-plugin-rxjs: "npm:5.0.3" @@ -1127,6 +1129,21 @@ __metadata: languageName: node linkType: hard +"@stylistic/eslint-plugin@npm:2.9.0": + version: 2.9.0 + resolution: "@stylistic/eslint-plugin@npm:2.9.0" + dependencies: + "@typescript-eslint/utils": "npm:^8.8.0" + eslint-visitor-keys: "npm:^4.1.0" + espree: "npm:^10.2.0" + estraverse: "npm:^5.3.0" + picomatch: "npm:^4.0.2" + peerDependencies: + eslint: ">=8.40.0" + checksum: 10c0/334e15502732961032355fabecf8eb03652f9e0cfea5bcb8c975402796625762de10d32b43c1e5b28c0cb1772cdbbc6f2c765e9f7bffb9170d579fe4e9f013ab + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -1433,6 +1450,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/scope-manager@npm:8.9.0": + version: 8.9.0 + resolution: "@typescript-eslint/scope-manager@npm:8.9.0" + dependencies: + "@typescript-eslint/types": "npm:8.9.0" + "@typescript-eslint/visitor-keys": "npm:8.9.0" + checksum: 10c0/1fb77a982e3384d8cabd64678ea8f9de328708080ff9324bf24a44da4e8d7b7692ae4820efc3ef36027bf0fd6a061680d3c30ce63d661fb31e18970fca5e86c5 + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.8.0": version: 8.8.0 resolution: "@typescript-eslint/type-utils@npm:8.8.0" @@ -1469,6 +1496,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.9.0, @typescript-eslint/types@npm:^8.9.0": + version: 8.9.0 + resolution: "@typescript-eslint/types@npm:8.9.0" + checksum: 10c0/8d901b7ed2f943624c24f7fa67f7be9d49a92554d54c4f27397c05b329ceff59a9ea246810b53ff36fca08760c14305dd4ce78fbac7ca0474311b0575bf49010 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.58.0": version: 5.58.0 resolution: "@typescript-eslint/typescript-estree@npm:5.58.0" @@ -1525,6 +1559,25 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:8.9.0": + version: 8.9.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.9.0" + dependencies: + "@typescript-eslint/types": "npm:8.9.0" + "@typescript-eslint/visitor-keys": "npm:8.9.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/bb5ec70727f07d1575e95f9d117762636209e1ab073a26c4e873e1e5b4617b000d300a23d294ad81693f7e99abe3e519725452c30b235a253edcd85b6ae052b0 + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:5.58.0": version: 5.58.0 resolution: "@typescript-eslint/utils@npm:5.58.0" @@ -1571,6 +1624,20 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/utils@npm:^8.8.0, @typescript-eslint/utils@npm:^8.9.0": + version: 8.9.0 + resolution: "@typescript-eslint/utils@npm:8.9.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.9.0" + "@typescript-eslint/types": "npm:8.9.0" + "@typescript-eslint/typescript-estree": "npm:8.9.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + checksum: 10c0/af13e3d501060bdc5fa04b131b3f9a90604e5c1d4845d1f8bd94b703a3c146a76debfc21fe65a7f3a0459ed6c57cf2aa3f0a052469bb23b6f35ff853fe9495b1 + languageName: node + linkType: hard + "@typescript-eslint/visitor-keys@npm:5.58.0": version: 5.58.0 resolution: "@typescript-eslint/visitor-keys@npm:5.58.0" @@ -1601,6 +1668,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.9.0": + version: 8.9.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.9.0" + dependencies: + "@typescript-eslint/types": "npm:8.9.0" + eslint-visitor-keys: "npm:^3.4.3" + checksum: 10c0/e33208b946841f1838d87d64f4ee230f798e68bdce8c181d3ac0abb567f758cb9c4bdccc919d493167869f413ca4c400e7db0f7dd7e8fc84ab6a8344076a7458 + languageName: node + linkType: hard + "@vue/eslint-config-typescript@npm:14.0.0, @vue/eslint-config-typescript@npm:^14.0.0": version: 14.0.0 resolution: "@vue/eslint-config-typescript@npm:14.0.0" @@ -3199,6 +3276,33 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-perfectionist@npm:3.9.0": + version: 3.9.0 + resolution: "eslint-plugin-perfectionist@npm:3.9.0" + dependencies: + "@typescript-eslint/types": "npm:^8.9.0" + "@typescript-eslint/utils": "npm:^8.9.0" + minimatch: "npm:^9.0.5" + natural-compare-lite: "npm:^1.4.0" + peerDependencies: + astro-eslint-parser: ^1.0.2 + eslint: ">=8.0.0" + svelte: ">=3.0.0" + svelte-eslint-parser: ^0.41.1 + vue-eslint-parser: ">=9.0.0" + peerDependenciesMeta: + astro-eslint-parser: + optional: true + svelte: + optional: true + svelte-eslint-parser: + optional: true + vue-eslint-parser: + optional: true + checksum: 10c0/befafd9ecb8075362461da7dc07ecced0f44e34fceb821eb2734ca384983dc1062cc23788d1ba267788fd252a78f267302934b314f0f2012d3f4303bd13e775b + languageName: node + linkType: hard + "eslint-plugin-react-hooks@npm:5.0.0, eslint-plugin-react-hooks@npm:^5.0.0": version: 5.0.0 resolution: "eslint-plugin-react-hooks@npm:5.0.0" @@ -3379,7 +3483,7 @@ __metadata: languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.1.0": +"espree@npm:^10.0.1, espree@npm:^10.1.0, espree@npm:^10.2.0": version: 10.2.0 resolution: "espree@npm:10.2.0" dependencies: @@ -5494,6 +5598,13 @@ __metadata: languageName: node linkType: hard +"natural-compare-lite@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare-lite@npm:1.4.0" + checksum: 10c0/f6cef26f5044515754802c0fc475d81426f3b90fe88c20fabe08771ce1f736ce46e0397c10acb569a4dd0acb84c7f1ee70676122f95d5bfdd747af3a6c6bbaa8 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -5866,6 +5977,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc + languageName: node + linkType: hard + "pidtree@npm:~0.6.0": version: 0.6.0 resolution: "pidtree@npm:0.6.0"