mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
feat(monorepo): migrate vue packages and apply oxlint refactors
This commit is contained in:
@@ -41,6 +41,19 @@ compose(base, typescript, {
|
||||
| `imports` | Import rules (cycles, duplicates, ordering) |
|
||||
| `node` | Node.js-specific rules |
|
||||
|
||||
## Rules Documentation
|
||||
|
||||
Подробные описания правил и `good/bad` примеры вынесены в отдельную директорию:
|
||||
|
||||
- `rules/README.md`
|
||||
- `rules/base.md`
|
||||
- `rules/typescript.md`
|
||||
- `rules/vue.md`
|
||||
- `rules/vitest.md`
|
||||
- `rules/imports.md`
|
||||
- `rules/node.md`
|
||||
- `rules/stylistic.md`
|
||||
|
||||
## API
|
||||
|
||||
### `compose(...configs: OxlintConfig[]): OxlintConfig`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { defineConfig } from 'oxlint';
|
||||
import { compose, base, typescript, imports } from '@robonen/oxlint';
|
||||
import { compose, base, typescript, imports, stylistic } from '@robonen/oxlint';
|
||||
|
||||
export default defineConfig(compose(base, typescript, imports));
|
||||
export default defineConfig(compose(base, typescript, imports, stylistic));
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"url": "git+https://github.com/robonen/tools.git",
|
||||
"directory": "configs/oxlint"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3",
|
||||
"packageManager": "pnpm@10.30.3",
|
||||
"engines": {
|
||||
"node": ">=24.13.1"
|
||||
},
|
||||
@@ -27,12 +27,13 @@
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "oxlint -c oxlint.config.ts",
|
||||
"lint:check": "oxlint -c oxlint.config.ts",
|
||||
"lint:fix": "oxlint -c oxlint.config.ts --fix",
|
||||
"test": "vitest run",
|
||||
"dev": "vitest dev",
|
||||
"build": "tsdown"
|
||||
@@ -41,11 +42,18 @@
|
||||
"@robonen/oxlint": "workspace:*",
|
||||
"@robonen/tsconfig": "workspace:*",
|
||||
"@robonen/tsdown": "workspace:*",
|
||||
"@stylistic/eslint-plugin": "catalog:",
|
||||
"oxlint": "catalog:",
|
||||
"tsdown": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"oxlint": ">=1.0.0"
|
||||
"oxlint": ">=1.0.0",
|
||||
"@stylistic/eslint-plugin": ">=4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@stylistic/eslint-plugin": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
21
configs/oxlint/rules/README.md
Normal file
21
configs/oxlint/rules/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Rules Reference
|
||||
|
||||
Документация по preset-ам `@robonen/oxlint`: что включает каждый preset и какие правила чаще всего влияют на код.
|
||||
|
||||
## Presets
|
||||
|
||||
- [base](./base.md)
|
||||
- [typescript](./typescript.md)
|
||||
- [vue](./vue.md)
|
||||
- [vitest](./vitest.md)
|
||||
- [imports](./imports.md)
|
||||
- [node](./node.md)
|
||||
- [stylistic](./stylistic.md)
|
||||
|
||||
## Как читать
|
||||
|
||||
- `Purpose` — зачем preset подключать.
|
||||
- `Key Rules` — ключевые правила из preset-а (не полный dump).
|
||||
- `Examples` — минимальные `good/bad` примеры.
|
||||
|
||||
Для точного источника правил см. файлы в `configs/oxlint/src/presets/*.ts`.
|
||||
34
configs/oxlint/rules/base.md
Normal file
34
configs/oxlint/rules/base.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# base preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Базовый quality-профиль для JS/TS-проектов: корректность, анти-паттерны, безопасные дефолты.
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `eslint/eqeqeq`: запрещает `==`, требует `===`.
|
||||
- `eslint/no-unused-vars`: запрещает неиспользуемые переменные (кроме `_name`).
|
||||
- `eslint/no-eval`, `eslint/no-var`, `eslint/prefer-const`.
|
||||
- `unicorn/prefer-node-protocol`: требует `node:` для built-in модулей.
|
||||
- `unicorn/no-thenable`: запрещает thenable-объекты.
|
||||
- `oxc/*` correctness правила (`bad-comparison-sequence`, `missing-throw` и др.).
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
const id = 42;
|
||||
if (id === 42) {
|
||||
throw new Error('unexpected');
|
||||
}
|
||||
|
||||
// ❌ Bad
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
var id = 42;
|
||||
if (id == '42') {
|
||||
throw 'unexpected';
|
||||
}
|
||||
```
|
||||
27
configs/oxlint/rules/imports.md
Normal file
27
configs/oxlint/rules/imports.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# imports preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Чистые границы модулей и предсказуемые импорты.
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `import/no-duplicates`.
|
||||
- `import/no-self-import`.
|
||||
- `import/no-cycle` (warn).
|
||||
- `import/no-mutable-exports`.
|
||||
- `import/consistent-type-specifier-style`: `prefer-top-level`.
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
import type { User } from './types';
|
||||
import { getUser } from './service';
|
||||
|
||||
// ❌ Bad
|
||||
import { getUser } from './service';
|
||||
import { getUser as getUser2 } from './service';
|
||||
|
||||
export let state = 0;
|
||||
```
|
||||
22
configs/oxlint/rules/node.md
Normal file
22
configs/oxlint/rules/node.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# node preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Node.js-правила и окружение `env.node = true`.
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `node/no-exports-assign`: запрещает перезапись `exports`.
|
||||
- `node/no-new-require`: запрещает `new require(...)`.
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
module.exports = { run };
|
||||
const mod = require('./mod');
|
||||
|
||||
// ❌ Bad
|
||||
exports = { run };
|
||||
const bad = new require('./mod');
|
||||
```
|
||||
51
configs/oxlint/rules/stylistic.md
Normal file
51
configs/oxlint/rules/stylistic.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# stylistic preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Форматирование через `@stylistic/eslint-plugin` (отступы, пробелы, скобки, переносы, TS/JSX-стиль).
|
||||
|
||||
## Defaults
|
||||
|
||||
- `indent: 2`
|
||||
- `quotes: single`
|
||||
- `semi: always`
|
||||
- `braceStyle: stroustrup`
|
||||
- `commaDangle: always-multiline`
|
||||
- `arrowParens: as-needed`
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `@stylistic/indent`, `@stylistic/no-tabs`.
|
||||
- `@stylistic/quotes`, `@stylistic/semi`.
|
||||
- `@stylistic/object-curly-spacing`, `@stylistic/comma-spacing`.
|
||||
- `@stylistic/arrow-spacing`, `@stylistic/space-before-function-paren`.
|
||||
- `@stylistic/max-statements-per-line`.
|
||||
- `@stylistic/no-mixed-operators`.
|
||||
- `@stylistic/member-delimiter-style` (TS).
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
type User = {
|
||||
id: string;
|
||||
role: 'admin' | 'user';
|
||||
};
|
||||
|
||||
const value = condition
|
||||
? 'yes'
|
||||
: 'no';
|
||||
|
||||
const sum = (a: number, b: number) => a + b;
|
||||
|
||||
// ❌ Bad
|
||||
type User = {
|
||||
id: string
|
||||
role: 'admin' | 'user'
|
||||
}
|
||||
|
||||
const value = condition ? 'yes' : 'no'; const x = 1;
|
||||
const sum=(a:number,b:number)=>{ return a+b };
|
||||
```
|
||||
|
||||
Полный список правил и их настройки см. в `src/presets/stylistic.ts`.
|
||||
33
configs/oxlint/rules/typescript.md
Normal file
33
configs/oxlint/rules/typescript.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# typescript preset
|
||||
|
||||
## Purpose
|
||||
|
||||
TypeScript-правила для `.ts/.tsx/.mts/.cts` через `overrides`.
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `typescript/consistent-type-imports`: выносит типы в `import type`.
|
||||
- `typescript/no-import-type-side-effects`: запрещает сайд-эффекты в type import.
|
||||
- `typescript/prefer-as-const`.
|
||||
- `typescript/no-namespace`, `typescript/triple-slash-reference`.
|
||||
- `typescript/no-wrapper-object-types`: запрещает `String`, `Number`, `Boolean`.
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
import type { User } from './types';
|
||||
|
||||
const status = 'ok' as const;
|
||||
interface Payload {
|
||||
value: string;
|
||||
}
|
||||
|
||||
// ❌ Bad
|
||||
import { User } from './types';
|
||||
|
||||
type Boxed = String;
|
||||
namespace Legacy {
|
||||
export const x = 1;
|
||||
}
|
||||
```
|
||||
34
configs/oxlint/rules/vitest.md
Normal file
34
configs/oxlint/rules/vitest.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# vitest preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Правила для тестов (`*.test.*`, `*.spec.*`, `test/**`, `__tests__/**`).
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `vitest/no-conditional-tests`.
|
||||
- `vitest/no-import-node-test`.
|
||||
- `vitest/prefer-to-be-truthy`, `vitest/prefer-to-be-falsy`.
|
||||
- `vitest/prefer-to-have-length`.
|
||||
- Relaxations: `eslint/no-unused-vars` и `typescript/no-explicit-any` выключены для тестов.
|
||||
|
||||
## Examples
|
||||
|
||||
```ts
|
||||
// ✅ Good
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('list', () => {
|
||||
it('has items', () => {
|
||||
expect([1, 2, 3]).toHaveLength(3);
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
// ❌ Bad
|
||||
if (process.env.CI) {
|
||||
it('conditionally runs', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
}
|
||||
```
|
||||
30
configs/oxlint/rules/vue.md
Normal file
30
configs/oxlint/rules/vue.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# vue preset
|
||||
|
||||
## Purpose
|
||||
|
||||
Правила для Vue 3 с упором на Composition API и `<script setup>`.
|
||||
|
||||
## Key Rules
|
||||
|
||||
- `vue/no-export-in-script-setup`.
|
||||
- `vue/no-import-compiler-macros`.
|
||||
- `vue/define-props-declaration`: type-based.
|
||||
- `vue/define-emits-declaration`: type-based.
|
||||
- `vue/valid-define-props`, `vue/valid-define-emits`.
|
||||
- `vue/no-lifecycle-after-await`.
|
||||
|
||||
## Examples
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ id: string }>();
|
||||
const emit = defineEmits<{ change: [value: string] }>();
|
||||
</script>
|
||||
|
||||
<!-- ❌ Bad -->
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from 'vue';
|
||||
export const x = 1;
|
||||
const props = defineProps({ id: String });
|
||||
</script>
|
||||
```
|
||||
@@ -31,6 +31,7 @@ function deepMerge(target: Record<string, unknown>, source: Record<string, unkno
|
||||
* Compose multiple oxlint configurations into a single config.
|
||||
*
|
||||
* - `plugins` — union (deduplicated)
|
||||
* - `jsPlugins` — union (deduplicated by specifier)
|
||||
* - `categories` — later configs override earlier
|
||||
* - `rules` — later configs override earlier
|
||||
* - `overrides` — concatenated
|
||||
@@ -60,6 +61,22 @@ export function compose(...configs: OxlintConfig[]): OxlintConfig {
|
||||
result.plugins = Array.from(new Set([...(result.plugins ?? []), ...config.plugins]));
|
||||
}
|
||||
|
||||
// JS Plugins — union with dedup by specifier
|
||||
if (config.jsPlugins?.length) {
|
||||
const existing = result.jsPlugins ?? [];
|
||||
const seen = new Set(existing.map(e => typeof e === 'string' ? e : e.specifier));
|
||||
|
||||
for (const entry of config.jsPlugins) {
|
||||
const specifier = typeof entry === 'string' ? entry : entry.specifier;
|
||||
if (!seen.has(specifier)) {
|
||||
seen.add(specifier);
|
||||
existing.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
result.jsPlugins = existing;
|
||||
}
|
||||
|
||||
// Categories — shallow merge
|
||||
if (config.categories) {
|
||||
result.categories = { ...result.categories, ...config.categories };
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
export { compose } from './compose';
|
||||
|
||||
/* Presets */
|
||||
export { base, typescript, vue, vitest, imports, node } from './presets';
|
||||
export { base, typescript, vue, vitest, imports, node, stylistic } from './presets';
|
||||
|
||||
/* Types */
|
||||
export type {
|
||||
@@ -10,6 +10,7 @@ export type {
|
||||
OxlintOverride,
|
||||
OxlintEnv,
|
||||
OxlintGlobals,
|
||||
ExternalPluginEntry,
|
||||
AllowWarnDeny,
|
||||
DummyRule,
|
||||
DummyRuleMap,
|
||||
|
||||
@@ -16,5 +16,7 @@ export const imports: OxlintConfig = {
|
||||
'import/no-commonjs': 'warn',
|
||||
'import/no-empty-named-blocks': 'warn',
|
||||
'import/consistent-type-specifier-style': ['warn', 'prefer-top-level'],
|
||||
|
||||
'sort-imports': ['warn', { ignoreDeclarationSort: false, ignoreMemberSort: false, ignoreCase: true, allowSeparatedGroups: true }],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4,3 +4,4 @@ export { vue } from './vue';
|
||||
export { vitest } from './vitest';
|
||||
export { imports } from './imports';
|
||||
export { node } from './node';
|
||||
export { stylistic } from './stylistic';
|
||||
|
||||
162
configs/oxlint/src/presets/stylistic.ts
Normal file
162
configs/oxlint/src/presets/stylistic.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import type { OxlintConfig } from '../types';
|
||||
|
||||
/**
|
||||
* Stylistic formatting rules via `@stylistic/eslint-plugin`.
|
||||
*
|
||||
* Uses the plugin's `customize()` defaults:
|
||||
* - indent: 2
|
||||
* - quotes: single
|
||||
* - semi: true
|
||||
* - braceStyle: stroustrup
|
||||
* - commaDangle: always-multiline
|
||||
* - arrowParens: false (as-needed)
|
||||
* - blockSpacing: true
|
||||
* - quoteProps: consistent-as-needed
|
||||
* - jsx: true
|
||||
*
|
||||
* Requires `@stylistic/eslint-plugin` to be installed.
|
||||
*
|
||||
* @see https://eslint.style/guide/config-presets
|
||||
*/
|
||||
export const stylistic: OxlintConfig = {
|
||||
jsPlugins: ['@stylistic/eslint-plugin'],
|
||||
|
||||
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', 'consistent-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',
|
||||
}],
|
||||
},
|
||||
};
|
||||
@@ -11,6 +11,7 @@ export type {
|
||||
OxlintOverride,
|
||||
OxlintEnv,
|
||||
OxlintGlobals,
|
||||
ExternalPluginEntry,
|
||||
AllowWarnDeny,
|
||||
DummyRule,
|
||||
DummyRuleMap,
|
||||
|
||||
@@ -143,4 +143,29 @@ describe('compose', () => {
|
||||
expect(result.env).toBeUndefined();
|
||||
expect(result.settings).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should concatenate jsPlugins with dedup by specifier', () => {
|
||||
const a: OxlintConfig = { jsPlugins: ['eslint-plugin-foo'] };
|
||||
const b: OxlintConfig = { jsPlugins: ['eslint-plugin-foo', 'eslint-plugin-bar'] };
|
||||
|
||||
const result = compose(a, b);
|
||||
expect(result.jsPlugins).toEqual(['eslint-plugin-foo', 'eslint-plugin-bar']);
|
||||
});
|
||||
|
||||
it('should dedup jsPlugins with mixed string and object entries', () => {
|
||||
const a: OxlintConfig = { jsPlugins: ['eslint-plugin-foo'] };
|
||||
const b: OxlintConfig = { jsPlugins: [{ name: 'foo', specifier: 'eslint-plugin-foo' }] };
|
||||
|
||||
const result = compose(a, b);
|
||||
expect(result.jsPlugins).toEqual(['eslint-plugin-foo']);
|
||||
});
|
||||
|
||||
it('should keep jsPlugins and plugins independent', () => {
|
||||
const a: OxlintConfig = { plugins: ['eslint'], jsPlugins: ['eslint-plugin-foo'] };
|
||||
const b: OxlintConfig = { plugins: ['typescript'], jsPlugins: ['eslint-plugin-bar'] };
|
||||
|
||||
const result = compose(a, b);
|
||||
expect(result.plugins).toEqual(['eslint', 'typescript']);
|
||||
expect(result.jsPlugins).toEqual(['eslint-plugin-foo', 'eslint-plugin-bar']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
{
|
||||
"extends": "@robonen/tsconfig/tsconfig.json"
|
||||
"extends": "@robonen/tsconfig/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"url": "git+https://github.com/robonen/tools.git",
|
||||
"directory": "packages/tsconfig"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3",
|
||||
"packageManager": "pnpm@10.30.3",
|
||||
"engines": {
|
||||
"node": ">=24.13.1"
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"url": "git+https://github.com/robonen/tools.git",
|
||||
"directory": "configs/tsdown"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3",
|
||||
"packageManager": "pnpm@10.30.3",
|
||||
"engines": {
|
||||
"node": ">=24.13.1"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user