chore(configs): migrate oxlint→eslint presets, refactor tsconfig
- Replace @robonen/oxlint with @robonen/eslint (composable ESLint flat-config presets: base, typescript, vue, vitest, imports, node, stylistic). - Plugins bundled as deps: typescript-eslint, eslint-plugin-vue, @vitest/eslint-plugin, eslint-plugin-import-x, eslint-plugin-n, eslint-plugin-unicorn, @stylistic/eslint-plugin. - @robonen/tsconfig: add base/dom/node/vue configs for composite project refs.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import type { FlatConfigArray, FlatConfigInput } from './types';
|
||||
|
||||
/**
|
||||
* Compose multiple ESLint flat configurations into a single flat config array.
|
||||
*
|
||||
* ESLint flat config is an ordered array where later entries override earlier
|
||||
* ones, so composition is a flatten: each preset (an array) and each inline
|
||||
* override (a single object) are concatenated in order. `undefined`/`null`
|
||||
* inputs are skipped, allowing conditional spreads.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { compose, base, typescript, vue } from '@robonen/eslint';
|
||||
*
|
||||
* export default compose(base, typescript, vue, {
|
||||
* rules: { 'no-console': 'off' },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function compose(...configs: Array<FlatConfigInput | false | null | undefined>): FlatConfigArray {
|
||||
const result: FlatConfigArray = [];
|
||||
|
||||
for (const config of configs) {
|
||||
if (!config)
|
||||
continue;
|
||||
|
||||
if (Array.isArray(config))
|
||||
result.push(...config);
|
||||
else
|
||||
result.push(config);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/* Compose */
|
||||
export { compose } from './compose';
|
||||
|
||||
/* Presets */
|
||||
export { base, ignores, typescript, vue, vitest, imports, node, stylistic } from './presets';
|
||||
|
||||
/* Types */
|
||||
export type {
|
||||
FlatConfig,
|
||||
FlatConfigArray,
|
||||
FlatConfigInput,
|
||||
Rules,
|
||||
} from './types';
|
||||
@@ -0,0 +1,101 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import js from '@eslint/js';
|
||||
import unicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
|
||||
/**
|
||||
* Globally ignored paths — build output, coverage, generated artifacts.
|
||||
*
|
||||
* A flat config entry with only `ignores` acts as a global ignore.
|
||||
*/
|
||||
export const ignores: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/ignores',
|
||||
ignores: [
|
||||
'**/dist/**',
|
||||
'**/coverage/**',
|
||||
'**/node_modules/**',
|
||||
'**/.nuxt/**',
|
||||
'**/.output/**',
|
||||
'**/storybook-static/**',
|
||||
'**/*.min.*',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Base configuration for any JavaScript/TypeScript project.
|
||||
*
|
||||
* Includes `@eslint/js` recommended rules (the analog of oxlint's
|
||||
* `correctness` category) plus opinionated `eslint` core and `unicorn` rules.
|
||||
*
|
||||
* > Note: oxlint's `oxc/*` rules have no ESLint equivalent and are dropped in
|
||||
* > this migration; their intent is largely covered by `@eslint/js` recommended
|
||||
* > and `unicorn`.
|
||||
*/
|
||||
export const base: FlatConfigArray = [
|
||||
...ignores,
|
||||
{
|
||||
name: 'robonen/base/setup',
|
||||
languageOptions: {
|
||||
ecmaVersion: 2024,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.es2024,
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
js.configs.recommended,
|
||||
{
|
||||
name: 'robonen/base/rules',
|
||||
plugins: {
|
||||
unicorn,
|
||||
},
|
||||
rules: {
|
||||
/* ── eslint core ──────────────────────────────────────── */
|
||||
eqeqeq: 'error',
|
||||
'no-console': 'warn',
|
||||
'no-debugger': 'error',
|
||||
'no-eval': 'error',
|
||||
'no-var': 'error',
|
||||
'prefer-const': 'error',
|
||||
'prefer-template': 'warn',
|
||||
'no-useless-constructor': 'warn',
|
||||
'no-useless-rename': 'warn',
|
||||
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
'no-self-compare': 'error',
|
||||
'no-template-curly-in-string': 'warn',
|
||||
'no-throw-literal': 'error',
|
||||
'no-return-assign': 'warn',
|
||||
'no-else-return': 'warn',
|
||||
'no-lonely-if': 'warn',
|
||||
'no-unneeded-ternary': 'warn',
|
||||
'prefer-object-spread': 'warn',
|
||||
'prefer-exponentiation-operator': 'warn',
|
||||
'no-useless-computed-key': 'warn',
|
||||
'no-useless-concat': 'warn',
|
||||
curly: 'off',
|
||||
|
||||
/* ── unicorn ──────────────────────────────────────────── */
|
||||
'unicorn/prefer-node-protocol': 'error',
|
||||
'unicorn/no-instanceof-builtins': 'error',
|
||||
'unicorn/no-new-array': 'error',
|
||||
'unicorn/prefer-array-flat-map': 'warn',
|
||||
'unicorn/prefer-array-flat': 'warn',
|
||||
'unicorn/prefer-includes': 'warn',
|
||||
'unicorn/prefer-string-slice': 'warn',
|
||||
'unicorn/prefer-string-starts-ends-with': 'warn',
|
||||
'unicorn/throw-new-error': 'error',
|
||||
'unicorn/error-message': 'warn',
|
||||
'unicorn/no-useless-spread': 'warn',
|
||||
'unicorn/no-useless-undefined': 'off',
|
||||
'unicorn/prefer-optional-catch-binding': 'warn',
|
||||
'unicorn/prefer-type-error': 'warn',
|
||||
'unicorn/no-thenable': 'error',
|
||||
'unicorn/prefer-number-properties': 'warn',
|
||||
'unicorn/prefer-global-this': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import importX, { createNodeResolver } from 'eslint-plugin-import-x';
|
||||
|
||||
/**
|
||||
* Import configuration for clean module boundaries.
|
||||
*
|
||||
* Uses `eslint-plugin-import-x` (the faster, modern fork) under the `import-x`
|
||||
* namespace, plus the core `sort-imports` rule. The Node resolver is configured
|
||||
* explicitly (flat config no longer ships a default) with TypeScript/Vue-aware
|
||||
* extensions so resolution-based rules (`no-cycle`, `no-self-import`) work.
|
||||
*/
|
||||
export const imports: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/imports',
|
||||
plugins: {
|
||||
'import-x': importX,
|
||||
},
|
||||
settings: {
|
||||
'import-x/resolver-next': [
|
||||
createNodeResolver({
|
||||
extensions: ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts', '.vue', '.json', '.node'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
rules: {
|
||||
'import-x/no-duplicates': 'error',
|
||||
'import-x/no-self-import': 'error',
|
||||
'import-x/no-cycle': 'warn',
|
||||
'import-x/first': 'warn',
|
||||
'import-x/no-mutable-exports': 'error',
|
||||
'import-x/no-amd': 'error',
|
||||
'import-x/no-commonjs': 'warn',
|
||||
'import-x/no-empty-named-blocks': 'warn',
|
||||
'import-x/consistent-type-specifier-style': ['warn', 'prefer-top-level'],
|
||||
|
||||
/* Only enforce member order within `{ … }`; declaration order is sorted
|
||||
by source path across the codebase, which core `sort-imports` (orders
|
||||
by first member name) would otherwise fight. */
|
||||
'sort-imports': ['warn', { ignoreDeclarationSort: true }],
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Vue SFCs may have two <script> blocks (`<script>` + `<script setup>`),
|
||||
which the parser concatenates — `import-x/first` then wrongly flags the
|
||||
second block's imports as out of place. Kept here (rather than in the
|
||||
`vue` preset) so it wins regardless of preset composition order. */
|
||||
name: 'robonen/imports/vue',
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'import-x/first': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,7 @@
|
||||
export { base, ignores } from './base';
|
||||
export { typescript } from './typescript';
|
||||
export { vue } from './vue';
|
||||
export { vitest } from './vitest';
|
||||
export { imports } from './imports';
|
||||
export { node } from './node';
|
||||
export { stylistic } from './stylistic';
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import nodePlugin from 'eslint-plugin-n';
|
||||
import globals from 'globals';
|
||||
|
||||
/**
|
||||
* Node.js-specific configuration.
|
||||
*
|
||||
* Registers `eslint-plugin-n` (the maintained successor of `eslint-plugin-node`)
|
||||
* under the `n` namespace and adds Node globals.
|
||||
*/
|
||||
export const node: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/node',
|
||||
plugins: {
|
||||
n: nodePlugin,
|
||||
},
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'n/no-exports-assign': 'error',
|
||||
'n/no-new-require': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,165 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import stylisticPlugin from '@stylistic/eslint-plugin';
|
||||
|
||||
/**
|
||||
* Stylistic formatting rules via `@stylistic/eslint-plugin`.
|
||||
*
|
||||
* Roughly equivalent to the plugin's `customize()` defaults:
|
||||
* - indent: 2
|
||||
* - quotes: single
|
||||
* - semi: true
|
||||
* - braceStyle: stroustrup
|
||||
* - commaDangle: always-multiline
|
||||
* - arrowParens: as-needed
|
||||
* - blockSpacing: true
|
||||
* - quoteProps: consistent-as-needed
|
||||
* - jsx: true
|
||||
*
|
||||
* @see https://eslint.style/guide/config-presets
|
||||
*/
|
||||
export const stylistic: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/stylistic',
|
||||
plugins: {
|
||||
'@stylistic': stylisticPlugin,
|
||||
},
|
||||
rules: {
|
||||
/* ── spacing & layout ─────────────────────────────────── */
|
||||
'@stylistic/array-bracket-spacing': ['error', 'never'],
|
||||
'@stylistic/arrow-spacing': ['error', { after: true, before: true }],
|
||||
'@stylistic/block-spacing': ['error', 'always'],
|
||||
'@stylistic/comma-spacing': ['error', { after: true, before: false }],
|
||||
'@stylistic/computed-property-spacing': ['error', 'never', { enforceForClassMembers: true }],
|
||||
'@stylistic/dot-location': ['error', 'property'],
|
||||
'@stylistic/key-spacing': ['error', { afterColon: true, beforeColon: false }],
|
||||
'@stylistic/keyword-spacing': ['error', { after: true, before: true }],
|
||||
'@stylistic/no-mixed-spaces-and-tabs': 'error',
|
||||
'@stylistic/no-multi-spaces': 'error',
|
||||
'@stylistic/no-trailing-spaces': 'error',
|
||||
'@stylistic/no-whitespace-before-property': 'error',
|
||||
'@stylistic/rest-spread-spacing': ['error', 'never'],
|
||||
'@stylistic/semi-spacing': ['error', { after: true, before: false }],
|
||||
'@stylistic/space-before-blocks': ['error', 'always'],
|
||||
'@stylistic/space-before-function-paren': ['error', { anonymous: 'always', asyncArrow: 'always', named: 'never' }],
|
||||
'@stylistic/space-in-parens': ['error', 'never'],
|
||||
'@stylistic/space-infix-ops': 'error',
|
||||
'@stylistic/space-unary-ops': ['error', { nonwords: false, words: true }],
|
||||
'@stylistic/template-curly-spacing': 'error',
|
||||
'@stylistic/template-tag-spacing': ['error', 'never'],
|
||||
|
||||
/* ── braces & blocks ──────────────────────────────────── */
|
||||
'@stylistic/brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
|
||||
'@stylistic/arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
|
||||
'@stylistic/no-extra-parens': ['error', 'functions'],
|
||||
'@stylistic/no-floating-decimal': 'error',
|
||||
'@stylistic/wrap-iife': ['error', 'any', { functionPrototypeMethods: true }],
|
||||
'@stylistic/new-parens': 'error',
|
||||
'@stylistic/padded-blocks': ['error', { blocks: 'never', classes: 'never', switches: 'never' }],
|
||||
|
||||
/* ── punctuation ──────────────────────────────────────── */
|
||||
'@stylistic/comma-dangle': ['error', 'always-multiline'],
|
||||
'@stylistic/comma-style': ['error', 'last'],
|
||||
'@stylistic/semi': ['error', 'always'],
|
||||
'@stylistic/quotes': ['error', 'single', { allowTemplateLiterals: 'always', avoidEscape: false }],
|
||||
'@stylistic/quote-props': ['error', 'as-needed'],
|
||||
|
||||
/* ── indentation ──────────────────────────────────────── */
|
||||
'@stylistic/indent': ['error', 2, {
|
||||
ArrayExpression: 1,
|
||||
CallExpression: { arguments: 1 },
|
||||
flatTernaryExpressions: false,
|
||||
FunctionDeclaration: { body: 1, parameters: 1, returnType: 1 },
|
||||
FunctionExpression: { body: 1, parameters: 1, returnType: 1 },
|
||||
ignoreComments: false,
|
||||
ignoredNodes: [
|
||||
'TSUnionType',
|
||||
'TSIntersectionType',
|
||||
],
|
||||
ImportDeclaration: 1,
|
||||
MemberExpression: 1,
|
||||
ObjectExpression: 1,
|
||||
offsetTernaryExpressions: true,
|
||||
outerIIFEBody: 1,
|
||||
SwitchCase: 1,
|
||||
tabLength: 2,
|
||||
VariableDeclarator: 1,
|
||||
}],
|
||||
'@stylistic/indent-binary-ops': ['error', 2],
|
||||
'@stylistic/no-tabs': 'error',
|
||||
|
||||
/* ── line breaks ──────────────────────────────────────── */
|
||||
'@stylistic/eol-last': 'error',
|
||||
'@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 0 }],
|
||||
'@stylistic/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
|
||||
'@stylistic/max-statements-per-line': ['error', { max: 1 }],
|
||||
'@stylistic/multiline-ternary': ['error', 'always-multiline'],
|
||||
'@stylistic/operator-linebreak': ['error', 'before'],
|
||||
'@stylistic/object-curly-spacing': ['error', 'always'],
|
||||
|
||||
/* ── generators ───────────────────────────────────────── */
|
||||
'@stylistic/generator-star-spacing': ['error', { after: true, before: false }],
|
||||
'@stylistic/yield-star-spacing': ['error', { after: true, before: false }],
|
||||
|
||||
/* ── operators & mixed ────────────────────────────────── */
|
||||
'@stylistic/no-mixed-operators': ['error', {
|
||||
allowSamePrecedence: true,
|
||||
groups: [
|
||||
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
|
||||
['&&', '||'],
|
||||
['in', 'instanceof'],
|
||||
],
|
||||
}],
|
||||
|
||||
/* ── typescript styling ───────────────────────────────── */
|
||||
'@stylistic/member-delimiter-style': ['error', {
|
||||
multiline: { delimiter: 'semi', requireLast: true },
|
||||
multilineDetection: 'brackets',
|
||||
overrides: {
|
||||
interface: {
|
||||
multiline: { delimiter: 'semi', requireLast: true },
|
||||
},
|
||||
},
|
||||
singleline: { delimiter: 'semi' },
|
||||
}],
|
||||
'@stylistic/type-annotation-spacing': ['error', {}],
|
||||
'@stylistic/type-generic-spacing': 'error',
|
||||
'@stylistic/type-named-tuple-spacing': 'error',
|
||||
|
||||
/* ── comments ─────────────────────────────────────────── */
|
||||
'@stylistic/spaced-comment': ['error', 'always', {
|
||||
block: { balanced: true, exceptions: ['*'], markers: ['!'] },
|
||||
line: { exceptions: ['/', '#'], markers: ['/'] },
|
||||
}],
|
||||
|
||||
/* ── jsx ───────────────────────────────────────────────── */
|
||||
'@stylistic/jsx-closing-bracket-location': 'error',
|
||||
'@stylistic/jsx-closing-tag-location': 'error',
|
||||
'@stylistic/jsx-curly-brace-presence': ['error', { propElementValues: 'always' }],
|
||||
'@stylistic/jsx-curly-newline': 'error',
|
||||
'@stylistic/jsx-curly-spacing': ['error', 'never'],
|
||||
'@stylistic/jsx-equals-spacing': 'error',
|
||||
'@stylistic/jsx-first-prop-new-line': 'error',
|
||||
'@stylistic/jsx-function-call-newline': ['error', 'multiline'],
|
||||
'@stylistic/jsx-indent-props': ['error', 2],
|
||||
'@stylistic/jsx-max-props-per-line': ['error', { maximum: 1, when: 'multiline' }],
|
||||
'@stylistic/jsx-one-expression-per-line': ['error', { allow: 'single-child' }],
|
||||
'@stylistic/jsx-quotes': 'error',
|
||||
'@stylistic/jsx-tag-spacing': ['error', {
|
||||
afterOpening: 'never',
|
||||
beforeClosing: 'never',
|
||||
beforeSelfClosing: 'always',
|
||||
closingSlash: 'never',
|
||||
}],
|
||||
'@stylistic/jsx-wrap-multilines': ['error', {
|
||||
arrow: 'parens-new-line',
|
||||
assignment: 'parens-new-line',
|
||||
condition: 'parens-new-line',
|
||||
declaration: 'parens-new-line',
|
||||
logical: 'parens-new-line',
|
||||
prop: 'parens-new-line',
|
||||
propertyValue: 'parens-new-line',
|
||||
return: 'parens-new-line',
|
||||
}],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
/**
|
||||
* TypeScript-specific configuration.
|
||||
*
|
||||
* Pulls in `typescript-eslint`'s recommended (non type-checked) setup — which
|
||||
* registers the parser/plugin and disables core rules superseded by their
|
||||
* TypeScript-aware counterparts — then layers opinionated rules on top.
|
||||
*
|
||||
* `.vue` files are included so the rules apply inside `<script lang="ts">`
|
||||
* blocks; the `vue` preset assigns the matching parser for them.
|
||||
*/
|
||||
export const typescript: FlatConfigArray = [
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
name: 'robonen/typescript',
|
||||
files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts', '**/*.vue'],
|
||||
rules: {
|
||||
/* core no-unused-vars is replaced by the TS-aware version */
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
|
||||
/* TypeScript already reports undefined names; `no-undef` only adds
|
||||
false positives (e.g. globals, auto-imports, compiler macros). */
|
||||
'no-undef': 'off',
|
||||
|
||||
'@typescript-eslint/consistent-type-imports': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/prefer-as-const': 'error',
|
||||
'@typescript-eslint/no-empty-object-type': ['warn', { allowInterfaces: 'with-single-extends' }],
|
||||
'@typescript-eslint/no-wrapper-object-types': 'error',
|
||||
'@typescript-eslint/no-duplicate-enum-values': 'error',
|
||||
'@typescript-eslint/no-unsafe-declaration-merging': 'error',
|
||||
'@typescript-eslint/no-import-type-side-effects': 'error',
|
||||
'@typescript-eslint/no-useless-empty-export': 'warn',
|
||||
'@typescript-eslint/no-inferrable-types': 'warn',
|
||||
'@typescript-eslint/prefer-function-type': 'warn',
|
||||
'@typescript-eslint/ban-tslint-comment': 'error',
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
'@typescript-eslint/prefer-for-of': 'warn',
|
||||
'@typescript-eslint/no-unnecessary-type-constraint': 'warn',
|
||||
'@typescript-eslint/adjacent-overload-signatures': 'warn',
|
||||
'@typescript-eslint/array-type': ['warn', { default: 'array-simple' }],
|
||||
'@typescript-eslint/no-this-alias': 'error',
|
||||
'@typescript-eslint/triple-slash-reference': 'error',
|
||||
'@typescript-eslint/no-namespace': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import vitestPlugin from '@vitest/eslint-plugin';
|
||||
|
||||
/**
|
||||
* Vitest configuration for test files.
|
||||
*
|
||||
* Scoped to common test file patterns; also relaxes a few strict rules that
|
||||
* are noisy in tests.
|
||||
*/
|
||||
export const vitest: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/vitest',
|
||||
files: [
|
||||
'**/*.test.{ts,tsx,js,jsx}',
|
||||
'**/*.spec.{ts,tsx,js,jsx}',
|
||||
'**/test/**/*.{ts,tsx,js,jsx}',
|
||||
'**/__tests__/**/*.{ts,tsx,js,jsx}',
|
||||
],
|
||||
plugins: {
|
||||
vitest: vitestPlugin,
|
||||
},
|
||||
rules: {
|
||||
'vitest/no-conditional-tests': 'warn',
|
||||
'vitest/no-import-node-test': 'error',
|
||||
'vitest/prefer-to-be-truthy': 'warn',
|
||||
'vitest/prefer-to-be-falsy': 'warn',
|
||||
'vitest/prefer-to-be-object': 'warn',
|
||||
'vitest/prefer-to-have-length': 'warn',
|
||||
'vitest/consistent-test-filename': 'warn',
|
||||
'vitest/prefer-describe-function-title': 'warn',
|
||||
|
||||
/* relax strict rules in tests */
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { FlatConfigArray } from '../types';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import vueParser from 'vue-eslint-parser';
|
||||
|
||||
/**
|
||||
* Vue.js configuration.
|
||||
*
|
||||
* Registers `eslint-plugin-vue` with `vue-eslint-parser` (delegating
|
||||
* `<script lang="ts">` to the TypeScript parser) and enables an opinionated
|
||||
* subset that enforces Composition API with `<script setup>` and type-based
|
||||
* declarations. Only the listed rules are turned on — the plugin's large
|
||||
* `recommended` set is intentionally not pulled in.
|
||||
*/
|
||||
export const vue: FlatConfigArray = [
|
||||
{
|
||||
name: 'robonen/vue/setup',
|
||||
files: ['**/*.vue'],
|
||||
plugins: {
|
||||
vue: pluginVue,
|
||||
},
|
||||
processor: pluginVue.processors['.vue'],
|
||||
languageOptions: {
|
||||
parser: vueParser,
|
||||
parserOptions: {
|
||||
parser: tseslint.parser,
|
||||
ecmaFeatures: { jsx: true },
|
||||
extraFileExtensions: ['.vue'],
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'robonen/vue/rules',
|
||||
files: ['**/*.vue'],
|
||||
rules: {
|
||||
'vue/no-arrow-functions-in-watch': 'error',
|
||||
'vue/no-deprecated-destroyed-lifecycle': 'error',
|
||||
'vue/no-export-in-script-setup': 'error',
|
||||
'vue/no-lifecycle-after-await': 'error',
|
||||
'vue/no-multiple-slot-args': 'error',
|
||||
'vue/no-import-compiler-macros': 'error',
|
||||
'vue/define-emits-declaration': ['error', 'type-based'],
|
||||
'vue/define-props-declaration': ['error', 'type-based'],
|
||||
'vue/prefer-import-from-vue': 'error',
|
||||
'vue/no-required-prop-with-default': 'warn',
|
||||
'vue/valid-define-emits': 'error',
|
||||
'vue/valid-define-props': 'error',
|
||||
'vue/require-typed-ref': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
/**
|
||||
* A single ESLint flat configuration object.
|
||||
*
|
||||
* @see https://eslint.org/docs/latest/use/configure/configuration-files
|
||||
*/
|
||||
export type FlatConfig = Linter.Config;
|
||||
|
||||
/**
|
||||
* An array of ESLint flat configuration objects — the shape ESLint
|
||||
* expects from an `eslint.config.ts` default export.
|
||||
*/
|
||||
export type FlatConfigArray = FlatConfig[];
|
||||
|
||||
/**
|
||||
* A flat config rules record (`Partial<Linter.RulesRecord>`).
|
||||
*/
|
||||
export type Rules = NonNullable<FlatConfig['rules']>;
|
||||
|
||||
/**
|
||||
* Accepts either a single flat config object or an array of them.
|
||||
*
|
||||
* Used by {@link compose} so presets (arrays) and inline overrides
|
||||
* (single objects) can be passed interchangeably.
|
||||
*/
|
||||
export type FlatConfigInput = FlatConfig | FlatConfigArray;
|
||||
Reference in New Issue
Block a user