@qstanay/eslint-config 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,29 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [1.1.0] - 2026-08-08
6
+
7
+ ### Added
8
+
9
+ - Vitest support via `test` option for `defineConfig()` and `nuxt()`
10
+ - Auto-enabled when `vitest` is installed (`test: false` to disable)
11
+ - Applies `@vitest/eslint-plugin` recommended rules to common test globs
12
+ - Declares Vitest globals (`describe`, `it`, `expect`, `vi`, …) for those files
13
+
14
+ ## [1.0.4] - 2026-07-27
15
+
16
+ ### Fixed
17
+
18
+ - Add browser globals when Vue is enabled so `document` / `window` in app entry files do not trip `no-undef` (especially with `typescript: false`)
19
+ - Never use `@typescript-eslint/parser` as the root parser for `.vue`; keep `vue-eslint-parser` as root and apply TS recommended rules via a separate Vue rules block
20
+ - Scope TypeScript shared rules to TS/Vue files only (do not apply `@typescript-eslint/*` to plain JS/MJS)
21
+ - Register `@typescript-eslint` on config objects that reference TS rules so flat config stays valid
22
+ - Sync `vue/html-indent` with `stylistic.indent` in both `defineConfig()` and `nuxt()`
23
+
24
+ ### Changed
25
+
26
+ - `TypedFlatConfigItem` now aliases `Linter.Config` instead of deprecated `Linter.FlatConfig`
27
+
5
28
  ## [1.0.3] - 2026-07-20
6
29
 
7
30
  ### Fixed
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @qstanay/eslint-config
2
2
 
3
+ [![npm](https://img.shields.io/npm/dm/@qstanay/eslint-config)](https://www.npmjs.com/package/@qstanay/eslint-config)
4
+
3
5
  Opinionated ESLint **flat config** for Nuxt apps, Vue apps, and TypeScript packages.
4
6
 
5
7
  Formatting is handled by ESLint via **@stylistic** (`eslint --fix`). Do **not** use Prettier alongside this preset unless you disable stylistic rules.
@@ -137,6 +139,7 @@ Main entry for Vue / TypeScript projects.
137
139
  | `vue` | `boolean` | auto (`vue` or `nuxt` installed) | Enable Vue rules |
138
140
  | `typescript` | `boolean` | auto (`typescript` installed) | Enable TypeScript rules |
139
141
  | `nuxt` | `boolean` | auto (`nuxt` installed) | Enable Nuxt plugin rule(s). Prefer `/nuxt` for Nuxt apps |
142
+ | `test` | `boolean` | auto (`vitest` installed) | Enable Vitest rules/globals for test files |
140
143
  | `strict` | `boolean` | `false` | Stricter TS rules (e.g. `no-magic-numbers`) |
141
144
  | `stylistic` | `false \| object` | enabled | Formatting rules (see below) |
142
145
  | `overrides` | `Linter.RulesRecord` | `{}` | Custom rules merged last over shared rules |
@@ -163,11 +166,28 @@ Layer for Nuxt apps used with `withNuxt(...nuxt())`.
163
166
  | Option | Type | Default | Description |
164
167
  |---|---|---|---|
165
168
  | `stylistic` | `false \| object` | enabled | Formatting rules (see below) |
169
+ | `test` | `boolean` | auto (`vitest` installed) | Enable Vitest rules/globals for test files |
166
170
  | `strict` | `boolean` | `false` | Stricter TS rules (e.g. `no-magic-numbers`) |
167
171
  | `overrides` | `Linter.RulesRecord` | `{}` | Custom rules merged last over shared rules |
168
172
  | `ignores` | `string[]` | `[]` | Extra ignore globs (plus defaults / `.gitignore`) |
169
173
 
170
- There are no `vue` / `typescript` flags here — `@nuxt/eslint` already provides that stack. This preset adds shared opinionated rules, Vue opinionated rules, and stylistic formatting.
174
+ There are no `vue` / `typescript` flags here — `@nuxt/eslint` already provides that stack. This preset adds shared opinionated rules, Vue opinionated rules, stylistic formatting, and optional Vitest support.
175
+
176
+ ### `test` (Vitest)
177
+
178
+ Auto-enabled when `vitest` is installed. Applies to common test globs:
179
+
180
+ - `**/*.{test,spec}.{js,ts,…}`
181
+ - `**/__tests__/**`
182
+ - `**/tests/**`, `**/test/**`
183
+
184
+ Includes `@vitest/eslint-plugin` recommended rules and Vitest globals (`describe`, `it`, `expect`, `vi`, …).
185
+
186
+ ```js
187
+ defineConfig({ test: true })
188
+ // or disable even if vitest is installed:
189
+ defineConfig({ test: false })
190
+ ```
171
191
 
172
192
  ### `stylistic`
173
193
 
@@ -288,6 +308,7 @@ Disable Prettier for the same file types (or uninstall / disable the Prettier ex
288
308
  - Import hygiene + autofixable `import/order` via `eslint-plugin-import-x` in `defineConfig` (Nuxt relies on `@nuxt/eslint` for its own import/TS/Vue stack)
289
309
  - `overrides` are applied in a trailing config block so they win over shared/Vue rules
290
310
  - Optional `strict: true` for noisier TypeScript checks like `no-magic-numbers`
311
+ - Optional Vitest layer (`test`, auto when `vitest` is installed): recommended rules + test globals
291
312
 
292
313
  ## Agent / setup checklist
293
314
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as StylisticOptions, i as OptionsConfig, n as nuxt, o as TypedFlatConfigItem, r as Awaitable } from "./nuxt-B4vvMBlj.mjs";
1
+ import { a as StylisticOptions, i as OptionsConfig, n as nuxt, o as TypedFlatConfigItem, r as Awaitable } from "./nuxt-Bxa1EDDq.mjs";
2
2
  import { FlatConfigComposer } from "eslint-flat-config-utils";
3
3
  //#region src/index.d.ts
4
4
  declare function defineConfig(options?: OptionsConfig, ...userConfigs: (Awaitable<TypedFlatConfigItem | TypedFlatConfigItem[]>)[]): FlatConfigComposer<TypedFlatConfigItem, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBAuBgB,aACd,UAAS,kBACN,cAAc,UAAU,sBAAsB,4BAAyB,mBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;iBA+BgB,aACd,UAAS,kBACN,cAAc,UAAU,sBAAsB,4BAAyB,mBAAA"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as resolveEnabled, c as typescriptFiles, d as ignores, i as has, l as stylistic, n as vueRules, o as resolveStylisticOptions, r as VUE_FILES, s as sourceFiles, t as nuxt, u as sharedRules } from "./nuxt-jcrXAC4G.mjs";
1
+ import { a as TYPESCRIPT_FILES, c as resolveEnabled, d as test, f as stylistic, h as ignores, i as typescript, l as resolveStylisticOptions, m as typescriptRules, n as vueRules, o as VUE_FILES, p as javascriptRules, r as loadTypescriptPlugin, s as has, t as nuxt, u as sourceFiles } from "./nuxt-dkt3q_AQ.mjs";
2
2
  import { FlatConfigComposer } from "eslint-flat-config-utils";
3
3
  //#region src/configs/imports.ts
4
4
  async function imports() {
@@ -39,8 +39,10 @@ async function imports() {
39
39
  * Base JS quality rules from `@eslint/js` recommended.
40
40
  * Declares Node + ES2022 globals so `no-undef` does not false-positive on
41
41
  * `console`, `process`, and other built-ins.
42
+ * When `browser` is true (Vue apps), also declare browser globals for entry
43
+ * files outside `.vue` SFCs (`main.js`, `app.ts`, etc.).
42
44
  */
43
- async function javascript() {
45
+ async function javascript(options = {}) {
44
46
  const jsMod = await import("@eslint/js");
45
47
  const globalsMod = await import("globals");
46
48
  const js = jsMod.default ?? jsMod;
@@ -49,7 +51,8 @@ async function javascript() {
49
51
  name: "qstanay/javascript",
50
52
  languageOptions: { globals: {
51
53
  ...globals.es2022,
52
- ...globals.nodeBuiltin
54
+ ...globals.nodeBuiltin,
55
+ ...options.browser ? globals.browser : {}
53
56
  } },
54
57
  rules: { ...js.configs?.recommended?.rules ?? {} }
55
58
  }];
@@ -65,39 +68,12 @@ async function nuxt$1() {
65
68
  }];
66
69
  }
67
70
  //#endregion
68
- //#region src/configs/typescript.ts
69
- function collectRules(configs) {
70
- return Object.assign({}, ...configs.filter((config) => config.rules).map((config) => config.rules));
71
- }
72
- async function typescript(options = {}) {
73
- const parserMod = await import("@typescript-eslint/parser");
74
- const pluginMod = await import("@typescript-eslint/eslint-plugin");
75
- const parser = parserMod.default ?? parserMod;
76
- const plugin = pluginMod.default ?? pluginMod;
77
- const recommended = plugin.configs?.["flat/recommended"] ?? [];
78
- const vueEnabled = options.vue === true;
79
- return [{
80
- name: "qstanay/typescript",
81
- files: typescriptFiles(vueEnabled),
82
- languageOptions: {
83
- parser,
84
- parserOptions: {
85
- sourceType: "module",
86
- ecmaVersion: "latest",
87
- ...vueEnabled ? { extraFileExtensions: [".vue"] } : {}
88
- }
89
- },
90
- plugins: { "@typescript-eslint": plugin },
91
- rules: collectRules(recommended)
92
- }];
93
- }
94
- //#endregion
95
71
  //#region src/configs/vue.ts
96
72
  /**
97
73
  * Vue layer based on the official flat recommended preset.
98
74
  * Must spread the full array — it includes setup, processor, and rule packs.
99
75
  */
100
- async function vue() {
76
+ async function vue(options = {}) {
101
77
  const vuePluginMod = await import("eslint-plugin-vue");
102
78
  const globalsMod = await import("globals");
103
79
  const vuePlugin = vuePluginMod.default ?? vuePluginMod;
@@ -117,7 +93,7 @@ async function vue() {
117
93
  extraFileExtensions: [".vue"]
118
94
  }
119
95
  },
120
- rules: { ...vueRules() }
96
+ rules: { ...vueRules({ indent: options.indent }) }
121
97
  }];
122
98
  }
123
99
  //#endregion
@@ -126,29 +102,42 @@ function defineConfig(options = {}, ...userConfigs) {
126
102
  const enableTs = resolveEnabled(options.typescript, has("typescript"));
127
103
  const enableVue = resolveEnabled(options.vue, has("vue") || has("nuxt"));
128
104
  const enableNuxt = resolveEnabled(options.nuxt, has("nuxt"));
105
+ const enableTest = resolveEnabled(options.test, has("vitest"));
129
106
  const stylisticOptions = resolveStylisticOptions(options.stylistic);
130
107
  const files = sourceFiles(enableVue);
108
+ const vueIndent = stylisticOptions ? stylisticOptions.indent ?? 2 : void 0;
131
109
  const configs = [];
132
110
  configs.push(Promise.resolve(ignores([...options.ignores ?? [], ...!enableVue ? VUE_FILES : []])));
133
- configs.push(javascript());
111
+ configs.push(javascript({ browser: enableVue }));
134
112
  if (enableTs) configs.push(typescript({ vue: enableVue }));
135
113
  configs.push(imports());
136
114
  if (stylisticOptions) configs.push(stylistic(stylisticOptions));
137
- if (enableVue) configs.push(vue());
115
+ if (enableVue) configs.push(vue({ indent: vueIndent }));
138
116
  if (enableNuxt) configs.push(nuxt$1());
117
+ if (enableTest) configs.push(test());
139
118
  configs.push(Promise.resolve([{
140
119
  name: "qstanay/shared-rules",
141
120
  files,
142
- rules: sharedRules({
143
- typescript: enableTs,
144
- strict: options.strict
145
- })
146
- }]));
147
- if (options.overrides) configs.push(Promise.resolve([{
148
- name: "qstanay/overrides",
149
- files,
150
- rules: options.overrides
121
+ rules: javascriptRules()
151
122
  }]));
123
+ if (enableTs) configs.push((async () => {
124
+ const plugin = await loadTypescriptPlugin();
125
+ return [{
126
+ name: "qstanay/shared-rules/typescript",
127
+ files: enableVue ? [...TYPESCRIPT_FILES, ...VUE_FILES] : [...TYPESCRIPT_FILES],
128
+ plugins: { "@typescript-eslint": plugin },
129
+ rules: typescriptRules({ strict: options.strict })
130
+ }];
131
+ })());
132
+ if (options.overrides) configs.push((async () => {
133
+ const plugins = enableTs ? { "@typescript-eslint": await loadTypescriptPlugin() } : void 0;
134
+ return [{
135
+ name: "qstanay/overrides",
136
+ files,
137
+ ...plugins ? { plugins } : {},
138
+ rules: options.overrides
139
+ }];
140
+ })());
152
141
  const composer = new FlatConfigComposer();
153
142
  composer.append(...configs, ...userConfigs);
154
143
  return composer;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["nuxt","javascriptConfig","typescriptConfig","stylisticConfig","vueConfig","nuxtConfig"],"sources":["../src/configs/imports.ts","../src/configs/javascript.ts","../src/configs/nuxt.ts","../src/configs/typescript.ts","../src/configs/vue.ts","../src/index.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '../types';\n\nexport async function imports(): Promise<TypedFlatConfigItem[]> {\n const mod = await import('eslint-plugin-import-x');\n const plugin = (mod as any).default ?? mod;\n\n return [\n {\n name: 'qstanay/imports',\n plugins: {\n import: plugin,\n },\n rules: {\n 'import/first': 'error',\n 'import/no-duplicates': 'error',\n 'import/no-mutable-exports': 'error',\n 'import/no-named-default': 'error',\n 'import/order': [\n 'error',\n {\n 'groups': [\n 'builtin',\n 'external',\n 'internal',\n ['parent', 'sibling', 'index'],\n 'object',\n 'type',\n ],\n 'newlines-between': 'always',\n 'alphabetize': {\n order: 'asc',\n caseInsensitive: true,\n },\n },\n ],\n },\n },\n ];\n}\n","import type { TypedFlatConfigItem } from '../types';\n\n/**\n * Base JS quality rules from `@eslint/js` recommended.\n * Declares Node + ES2022 globals so `no-undef` does not false-positive on\n * `console`, `process`, and other built-ins.\n */\nexport async function javascript(): Promise<TypedFlatConfigItem[]> {\n const jsMod = await import('@eslint/js');\n const globalsMod = await import('globals');\n\n const js = (jsMod as any).default ?? jsMod;\n const globals = (globalsMod as any).default ?? globalsMod;\n\n return [\n {\n name: 'qstanay/javascript',\n languageOptions: {\n globals: {\n ...globals.es2022,\n ...globals.nodeBuiltin,\n },\n },\n rules: {\n ...(js.configs?.recommended?.rules ?? {}),\n },\n },\n ];\n}\n","import type { TypedFlatConfigItem } from '../types';\n\nexport async function nuxt(): Promise<TypedFlatConfigItem[]> {\n const mod = await import('@nuxt/eslint-plugin');\n const plugin = (mod as any).default ?? mod;\n\n return [\n {\n name: 'qstanay/nuxt',\n plugins: {\n nuxt: plugin,\n },\n rules: {\n // keep this minimal; Nuxt base config already provides most of it\n 'nuxt/prefer-import-meta': 'error',\n },\n },\n ];\n}\n","import { typescriptFiles } from '../utils';\n\nimport type { TypedFlatConfigItem } from '../types';\nimport type { Linter } from 'eslint';\n\nfunction collectRules(configs: TypedFlatConfigItem[]): Linter.RulesRecord {\n return Object.assign(\n {},\n ...configs\n .filter(config => config.rules)\n .map(config => config.rules),\n );\n}\n\nexport async function typescript(options: {\n vue?: boolean;\n} = {}): Promise<TypedFlatConfigItem[]> {\n const parserMod = await import('@typescript-eslint/parser');\n const pluginMod = await import('@typescript-eslint/eslint-plugin');\n\n const parser = (parserMod as any).default ?? parserMod;\n const plugin = (pluginMod as any).default ?? pluginMod;\n\n const recommended = (plugin.configs?.['flat/recommended'] ?? []) as TypedFlatConfigItem[];\n const vueEnabled = options.vue === true;\n\n return [\n {\n name: 'qstanay/typescript',\n files: typescriptFiles(vueEnabled),\n languageOptions: {\n parser,\n parserOptions: {\n sourceType: 'module',\n ecmaVersion: 'latest',\n ...(vueEnabled\n ? { extraFileExtensions: ['.vue'] }\n : {}),\n },\n },\n plugins: {\n '@typescript-eslint': plugin,\n },\n rules: collectRules(recommended),\n },\n ];\n}\n","import { VUE_FILES } from '../utils';\nimport { vueRules } from './vue-rules';\n\nimport type { TypedFlatConfigItem } from '../types';\n\n/**\n * Vue layer based on the official flat recommended preset.\n * Must spread the full array — it includes setup, processor, and rule packs.\n */\nexport async function vue(): Promise<TypedFlatConfigItem[]> {\n const vuePluginMod = await import('eslint-plugin-vue');\n const globalsMod = await import('globals');\n\n const vuePlugin = (vuePluginMod as any).default ?? vuePluginMod;\n const globals = (globalsMod as any).default ?? globalsMod;\n\n const recommended = (vuePlugin.configs?.['flat/recommended'] ?? []) as TypedFlatConfigItem[];\n\n return [\n ...recommended,\n {\n name: 'qstanay/vue/rules',\n files: [...VUE_FILES],\n languageOptions: {\n globals: {\n ...globals.browser,\n },\n parserOptions: {\n // Keep vue-eslint-parser as the root parser (from recommended),\n // and parse <script lang=\"ts\"> with the TS parser.\n parser: {\n ts: '@typescript-eslint/parser',\n js: 'espree',\n },\n ecmaVersion: 'latest',\n sourceType: 'module',\n extraFileExtensions: ['.vue'],\n },\n },\n rules: {\n ...vueRules(),\n },\n },\n ];\n}\n","import { FlatConfigComposer } from 'eslint-flat-config-utils';\n\nimport { ignores } from './configs/ignores';\nimport { imports } from './configs/imports';\nimport { javascript as javascriptConfig } from './configs/javascript';\nimport { nuxt as nuxtConfig } from './configs/nuxt';\nimport { sharedRules } from './configs/shared-rules';\nimport { stylistic as stylisticConfig } from './configs/stylistic';\nimport { typescript as typescriptConfig } from './configs/typescript';\nimport { vue as vueConfig } from './configs/vue';\nimport {\n has,\n resolveEnabled,\n resolveStylisticOptions,\n sourceFiles,\n VUE_FILES,\n} from './utils';\n\nimport type { Awaitable, OptionsConfig, TypedFlatConfigItem } from './types';\n\nexport { nuxt } from './nuxt';\nexport * from './types';\n\nexport function defineConfig(\n options: OptionsConfig = {},\n ...userConfigs: (Awaitable<TypedFlatConfigItem | TypedFlatConfigItem[]>)[]\n) {\n const enableTs = resolveEnabled(options.typescript, has('typescript'));\n const enableVue = resolveEnabled(options.vue, has('vue') || has('nuxt'));\n const enableNuxt = resolveEnabled(options.nuxt, has('nuxt'));\n const stylisticOptions = resolveStylisticOptions(options.stylistic);\n const files = sourceFiles(enableVue);\n\n const configs: (Awaitable<TypedFlatConfigItem[]>)[] = [];\n\n // When Vue is off, skip SFCs entirely so espree/TS do not fatal-parse them.\n configs.push(Promise.resolve(ignores([\n ...(options.ignores ?? []),\n ...(!enableVue ? VUE_FILES : []),\n ])));\n configs.push(javascriptConfig());\n\n if (enableTs) {\n configs.push(typescriptConfig({ vue: enableVue }));\n }\n\n configs.push(imports());\n\n if (stylisticOptions) {\n configs.push(stylisticConfig(stylisticOptions));\n }\n\n if (enableVue) {\n configs.push(vueConfig());\n }\n\n if (enableNuxt) {\n configs.push(nuxtConfig());\n }\n\n configs.push(Promise.resolve([\n {\n name: 'qstanay/shared-rules',\n files,\n rules: sharedRules({\n typescript: enableTs,\n strict: options.strict,\n }),\n },\n ]));\n\n if (options.overrides) {\n // Trailing block so project overrides win over shared/vue/ts packs.\n configs.push(Promise.resolve([\n {\n name: 'qstanay/overrides',\n files,\n rules: options.overrides,\n },\n ]));\n }\n\n const composer = new FlatConfigComposer<TypedFlatConfigItem>();\n composer.append(...configs, ...userConfigs as any);\n return composer;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["nuxt","javascriptConfig","typescriptConfig","stylisticConfig","vueConfig","nuxtConfig","testConfig"],"sources":["../src/configs/imports.ts","../src/configs/javascript.ts","../src/configs/nuxt.ts","../src/configs/vue.ts","../src/index.ts"],"sourcesContent":["import type { TypedFlatConfigItem } from '../types';\n\nexport async function imports(): Promise<TypedFlatConfigItem[]> {\n const mod = await import('eslint-plugin-import-x');\n const plugin = (mod as any).default ?? mod;\n\n return [\n {\n name: 'qstanay/imports',\n plugins: {\n import: plugin,\n },\n rules: {\n 'import/first': 'error',\n 'import/no-duplicates': 'error',\n 'import/no-mutable-exports': 'error',\n 'import/no-named-default': 'error',\n 'import/order': [\n 'error',\n {\n 'groups': [\n 'builtin',\n 'external',\n 'internal',\n ['parent', 'sibling', 'index'],\n 'object',\n 'type',\n ],\n 'newlines-between': 'always',\n 'alphabetize': {\n order: 'asc',\n caseInsensitive: true,\n },\n },\n ],\n },\n },\n ];\n}\n","import type { TypedFlatConfigItem } from '../types';\n\n/**\n * Base JS quality rules from `@eslint/js` recommended.\n * Declares Node + ES2022 globals so `no-undef` does not false-positive on\n * `console`, `process`, and other built-ins.\n * When `browser` is true (Vue apps), also declare browser globals for entry\n * files outside `.vue` SFCs (`main.js`, `app.ts`, etc.).\n */\nexport async function javascript(options: {\n browser?: boolean;\n} = {}): Promise<TypedFlatConfigItem[]> {\n const jsMod = await import('@eslint/js');\n const globalsMod = await import('globals');\n\n const js = (jsMod as any).default ?? jsMod;\n const globals = (globalsMod as any).default ?? globalsMod;\n\n return [\n {\n name: 'qstanay/javascript',\n languageOptions: {\n globals: {\n ...globals.es2022,\n ...globals.nodeBuiltin,\n ...(options.browser ? globals.browser : {}),\n },\n },\n rules: {\n ...(js.configs?.recommended?.rules ?? {}),\n },\n },\n ];\n}\n","import type { TypedFlatConfigItem } from '../types';\n\nexport async function nuxt(): Promise<TypedFlatConfigItem[]> {\n const mod = await import('@nuxt/eslint-plugin');\n const plugin = (mod as any).default ?? mod;\n\n return [\n {\n name: 'qstanay/nuxt',\n plugins: {\n nuxt: plugin,\n },\n rules: {\n // keep this minimal; Nuxt base config already provides most of it\n 'nuxt/prefer-import-meta': 'error',\n },\n },\n ];\n}\n","import { VUE_FILES } from '../utils';\nimport { vueRules } from './vue-rules';\n\nimport type { TypedFlatConfigItem } from '../types';\n\n/**\n * Vue layer based on the official flat recommended preset.\n * Must spread the full array — it includes setup, processor, and rule packs.\n */\nexport async function vue(options: {\n indent?: 2 | 4;\n} = {}): Promise<TypedFlatConfigItem[]> {\n const vuePluginMod = await import('eslint-plugin-vue');\n const globalsMod = await import('globals');\n\n const vuePlugin = (vuePluginMod as any).default ?? vuePluginMod;\n const globals = (globalsMod as any).default ?? globalsMod;\n\n const recommended = (vuePlugin.configs?.['flat/recommended'] ?? []) as TypedFlatConfigItem[];\n\n return [\n ...recommended,\n {\n name: 'qstanay/vue/rules',\n files: [...VUE_FILES],\n languageOptions: {\n globals: {\n ...globals.browser,\n },\n parserOptions: {\n // Keep vue-eslint-parser as the root parser (from recommended),\n // and parse <script lang=\"ts\"> with the TS parser.\n parser: {\n ts: '@typescript-eslint/parser',\n js: 'espree',\n },\n ecmaVersion: 'latest',\n sourceType: 'module',\n extraFileExtensions: ['.vue'],\n },\n },\n rules: {\n ...vueRules({ indent: options.indent }),\n },\n },\n ];\n}\n","import { FlatConfigComposer } from 'eslint-flat-config-utils';\n\nimport { ignores } from './configs/ignores';\nimport { imports } from './configs/imports';\nimport { javascript as javascriptConfig } from './configs/javascript';\nimport { nuxt as nuxtConfig } from './configs/nuxt';\nimport {\n javascriptRules,\n typescriptRules,\n} from './configs/shared-rules';\nimport { stylistic as stylisticConfig } from './configs/stylistic';\nimport { test as testConfig } from './configs/test';\nimport {\n loadTypescriptPlugin,\n typescript as typescriptConfig,\n} from './configs/typescript';\nimport { vue as vueConfig } from './configs/vue';\nimport {\n has,\n resolveEnabled,\n resolveStylisticOptions,\n sourceFiles,\n TYPESCRIPT_FILES,\n VUE_FILES,\n} from './utils';\n\nimport type { Awaitable, OptionsConfig, TypedFlatConfigItem } from './types';\n\nexport { nuxt } from './nuxt';\nexport * from './types';\n\nexport function defineConfig(\n options: OptionsConfig = {},\n ...userConfigs: (Awaitable<TypedFlatConfigItem | TypedFlatConfigItem[]>)[]\n) {\n const enableTs = resolveEnabled(options.typescript, has('typescript'));\n const enableVue = resolveEnabled(options.vue, has('vue') || has('nuxt'));\n const enableNuxt = resolveEnabled(options.nuxt, has('nuxt'));\n const enableTest = resolveEnabled(options.test, has('vitest'));\n const stylisticOptions = resolveStylisticOptions(options.stylistic);\n const files = sourceFiles(enableVue);\n const vueIndent = stylisticOptions\n ? (stylisticOptions.indent ?? 2)\n : undefined;\n\n const configs: (Awaitable<TypedFlatConfigItem[]>)[] = [];\n\n // When Vue is off, skip SFCs entirely so espree/TS do not fatal-parse them.\n configs.push(Promise.resolve(ignores([\n ...(options.ignores ?? []),\n ...(!enableVue ? VUE_FILES : []),\n ])));\n configs.push(javascriptConfig({ browser: enableVue }));\n\n if (enableTs) {\n configs.push(typescriptConfig({ vue: enableVue }));\n }\n\n configs.push(imports());\n\n if (stylisticOptions) {\n configs.push(stylisticConfig(stylisticOptions));\n }\n\n if (enableVue) {\n configs.push(vueConfig({ indent: vueIndent }));\n }\n\n if (enableNuxt) {\n configs.push(nuxtConfig());\n }\n\n if (enableTest) {\n configs.push(testConfig());\n }\n\n configs.push(Promise.resolve([\n {\n name: 'qstanay/shared-rules',\n files,\n rules: javascriptRules(),\n },\n ]));\n\n if (enableTs) {\n // TS opinionated rules only on TS sources (+ .vue when Vue is on).\n // Never apply them to plain JS/MJS — that needs type-aware parser services.\n configs.push((async () => {\n const plugin = await loadTypescriptPlugin();\n return [\n {\n name: 'qstanay/shared-rules/typescript',\n files: enableVue\n ? [...TYPESCRIPT_FILES, ...VUE_FILES]\n : [...TYPESCRIPT_FILES],\n plugins: {\n '@typescript-eslint': plugin,\n },\n rules: typescriptRules({ strict: options.strict }),\n },\n ];\n })());\n }\n\n if (options.overrides) {\n // Trailing block so project overrides win over shared/vue/ts packs.\n configs.push((async () => {\n const plugins = enableTs\n ? { '@typescript-eslint': await loadTypescriptPlugin() }\n : undefined;\n\n return [\n {\n name: 'qstanay/overrides',\n files,\n ...(plugins ? { plugins } : {}),\n rules: options.overrides,\n },\n ];\n })());\n }\n\n const composer = new FlatConfigComposer<TypedFlatConfigItem>();\n composer.append(...configs, ...userConfigs as any);\n return composer;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { Linter } from "eslint";
2
2
  //#region src/types.d.ts
3
3
  type Awaitable<T> = T | Promise<T>;
4
- type TypedFlatConfigItem = Linter.FlatConfig;
4
+ type TypedFlatConfigItem = Linter.Config;
5
5
  interface StylisticOptions {
6
6
  indent?: 2 | 4;
7
7
  quotes?: 'single' | 'double';
@@ -26,6 +26,11 @@ interface OptionsConfig {
26
26
  * Off by default to keep app/UI code quiet.
27
27
  */
28
28
  strict?: boolean;
29
+ /**
30
+ * Enable Vitest rules/globals for test files.
31
+ * Auto-enabled when `vitest` is installed unless explicitly set.
32
+ */
33
+ test?: boolean;
29
34
  stylistic?: boolean | StylisticOptions;
30
35
  /**
31
36
  * Extra ignores appended to defaults.
@@ -44,6 +49,11 @@ interface NuxtPresetOptions {
44
49
  * Enable stricter opinionated TypeScript rules (e.g. `no-magic-numbers`).
45
50
  */
46
51
  strict?: boolean;
52
+ /**
53
+ * Enable Vitest rules/globals for test files.
54
+ * Auto-enabled when `vitest` is installed unless explicitly set.
55
+ */
56
+ test?: boolean;
47
57
  overrides?: Linter.RulesRecord;
48
58
  /**
49
59
  * Extra ignores appended to defaults / `.gitignore`.
@@ -60,4 +70,4 @@ interface NuxtPresetOptions {
60
70
  declare function nuxt(options?: NuxtPresetOptions): Promise<TypedFlatConfigItem[]>[];
61
71
  //#endregion
62
72
  export { StylisticOptions as a, OptionsConfig as i, nuxt as n, TypedFlatConfigItem as o, Awaitable as r, NuxtPresetOptions as t };
63
- //# sourceMappingURL=nuxt-B4vvMBlj.d.mts.map
73
+ //# sourceMappingURL=nuxt-Bxa1EDDq.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nuxt-Bxa1EDDq.d.mts","names":[],"sources":["../src/types.ts","../src/nuxt.ts"],"mappings":";;KAEY,UAAU,KAAK,IAAI,QAAQ;KAE3B,sBAAsB,OAAO;UAExB;EACf;EACA;EACA;EACA;;UAGe;;;;EAIf;;;;EAIA;;;;EAIA;;;;;EAKA;;;;;EAKA;EACA,sBAAsB;;;;EAItB;;;;EAIA,YAAY,OAAO;;;;UCvBJ;EACf,sBAAsB;;;;EAItB;;;;;EAKA;EACA,YAAY,OAAO;;;;EAInB;;;;;;;;;iBAUc,KAAK,UAAS,oBAAsB,QAAA"}
@@ -62,17 +62,6 @@ function typescriptRules(options = {}) {
62
62
  }];
63
63
  return rules;
64
64
  }
65
- /**
66
- * Shared opinionated rules.
67
- * TypeScript rules are included only when `typescript` is true,
68
- * so JS-only projects do not reference a missing plugin.
69
- */
70
- function sharedRules(options = {}) {
71
- return {
72
- ...javascriptRules(),
73
- ...options.typescript ? typescriptRules({ strict: options.strict }) : {}
74
- };
75
- }
76
65
  //#endregion
77
66
  //#region src/configs/stylistic.ts
78
67
  async function loadStylistic() {
@@ -107,6 +96,35 @@ async function stylistic(options = {}) {
107
96
  }];
108
97
  }
109
98
  //#endregion
99
+ //#region src/configs/test.ts
100
+ const TEST_FILES = [
101
+ "**/__tests__/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}",
102
+ "**/*.{test,spec}.{js,mjs,cjs,jsx,ts,tsx,cts,mts}",
103
+ "**/tests/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}",
104
+ "**/test/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}"
105
+ ];
106
+ /**
107
+ * Vitest layer: recommended rules + test globals for common test globs.
108
+ */
109
+ async function test() {
110
+ const mod = await import("@vitest/eslint-plugin");
111
+ const vitest = mod.default ?? mod;
112
+ const globalsMod = await import("globals");
113
+ const globals = globalsMod.default ?? globalsMod;
114
+ const recommended = vitest.configs?.recommended ?? {};
115
+ const env = vitest.configs?.env ?? {};
116
+ return [{
117
+ name: "qstanay/test",
118
+ files: [...TEST_FILES],
119
+ plugins: { vitest },
120
+ languageOptions: { globals: {
121
+ ...globals.vitest,
122
+ ...env.languageOptions?.globals ?? {}
123
+ } },
124
+ rules: { ...recommended.rules ?? {} }
125
+ }];
126
+ }
127
+ //#endregion
110
128
  //#region src/utils.ts
111
129
  function resolveEnabled(explicit, detected) {
112
130
  return explicit ?? detected;
@@ -121,7 +139,7 @@ function resolveStylisticOptions(stylistic) {
121
139
  }
122
140
  const SCRIPT_FILES = ["**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}"];
123
141
  const VUE_FILES = ["**/*.vue"];
124
- /** TypeScript sources (`.vue` is added only when Vue is enabled). */
142
+ /** TypeScript sources — never includes `.vue` (Vue keeps its own root parser). */
125
143
  const TYPESCRIPT_FILES = ["**/*.{ts,tsx,cts,mts}"];
126
144
  /**
127
145
  * Files that receive shared / override rule packs.
@@ -130,15 +148,57 @@ const TYPESCRIPT_FILES = ["**/*.{ts,tsx,cts,mts}"];
130
148
  function sourceFiles(vueEnabled) {
131
149
  return vueEnabled ? [...SCRIPT_FILES, ...VUE_FILES] : [...SCRIPT_FILES];
132
150
  }
133
- function typescriptFiles(vueEnabled) {
134
- return vueEnabled ? [...TYPESCRIPT_FILES, ...VUE_FILES] : [...TYPESCRIPT_FILES];
135
- }
136
151
  /** Files that receive shared / override rule packs when Vue is enabled. */
137
152
  const SOURCE_FILES = ["**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts,vue}"];
138
153
  //#endregion
154
+ //#region src/configs/typescript.ts
155
+ function collectRules(configs) {
156
+ return Object.assign({}, ...configs.filter((config) => config.rules).map((config) => config.rules));
157
+ }
158
+ async function loadTypescriptPlugin() {
159
+ const pluginMod = await import("@typescript-eslint/eslint-plugin");
160
+ return pluginMod.default ?? pluginMod;
161
+ }
162
+ /**
163
+ * TypeScript layer.
164
+ *
165
+ * Important: `@typescript-eslint/parser` is never the root parser for `.vue`.
166
+ * Vue SFCs keep `vue-eslint-parser` as root (from the Vue layer) and use the
167
+ * TS parser only as a nested script parser.
168
+ *
169
+ * Flat config requires the plugin on every object that references
170
+ * `@typescript-eslint/*` rules (see shared-rules / overrides too).
171
+ */
172
+ async function typescript(options = {}) {
173
+ const parserMod = await import("@typescript-eslint/parser");
174
+ const plugin = await loadTypescriptPlugin();
175
+ const parser = parserMod.default ?? parserMod;
176
+ const rules = collectRules(plugin.configs?.["flat/recommended"] ?? []);
177
+ const configs = [{
178
+ name: "qstanay/typescript",
179
+ files: [...TYPESCRIPT_FILES],
180
+ languageOptions: {
181
+ parser,
182
+ parserOptions: {
183
+ sourceType: "module",
184
+ ecmaVersion: "latest"
185
+ }
186
+ },
187
+ plugins: { "@typescript-eslint": plugin },
188
+ rules
189
+ }];
190
+ if (options.vue) configs.push({
191
+ name: "qstanay/typescript/vue-rules",
192
+ files: [...VUE_FILES],
193
+ plugins: { "@typescript-eslint": plugin },
194
+ rules
195
+ });
196
+ return configs;
197
+ }
198
+ //#endregion
139
199
  //#region src/configs/vue-rules.ts
140
- function vueRules() {
141
- return {
200
+ function vueRules(options = {}) {
201
+ const rules = {
142
202
  "vue/html-self-closing": ["error", {
143
203
  html: {
144
204
  void: "always",
@@ -155,6 +215,8 @@ function vueRules() {
155
215
  next: "*"
156
216
  }]]
157
217
  };
218
+ if (options.indent !== void 0) rules["vue/html-indent"] = ["error", options.indent];
219
+ return rules;
158
220
  }
159
221
  //#endregion
160
222
  //#region src/nuxt.ts
@@ -167,20 +229,32 @@ function vueRules() {
167
229
  */
168
230
  function nuxt(options = {}) {
169
231
  const stylisticOptions = resolveStylisticOptions(options.stylistic);
232
+ const enableTest = resolveEnabled(options.test, has("vitest"));
233
+ const vueIndent = stylisticOptions ? stylisticOptions.indent ?? 2 : void 0;
170
234
  const configs = [Promise.resolve(ignores(options.ignores))];
171
235
  if (stylisticOptions) configs.push(stylistic(stylisticOptions));
172
- configs.push(Promise.resolve([{
173
- name: "qstanay/nuxt/shared-rules",
174
- files: [...SOURCE_FILES],
175
- rules: sharedRules({
176
- typescript: true,
177
- strict: options.strict
178
- })
179
- }, {
180
- name: "qstanay/nuxt/vue-rules",
181
- files: [...VUE_FILES],
182
- rules: { ...vueRules() }
183
- }]));
236
+ if (enableTest) configs.push(test());
237
+ configs.push((async () => {
238
+ const plugin = await loadTypescriptPlugin();
239
+ return [
240
+ {
241
+ name: "qstanay/nuxt/shared-rules",
242
+ files: [...SOURCE_FILES],
243
+ rules: javascriptRules()
244
+ },
245
+ {
246
+ name: "qstanay/nuxt/shared-rules/typescript",
247
+ files: [...TYPESCRIPT_FILES, ...VUE_FILES],
248
+ plugins: { "@typescript-eslint": plugin },
249
+ rules: typescriptRules({ strict: options.strict })
250
+ },
251
+ {
252
+ name: "qstanay/nuxt/vue-rules",
253
+ files: [...VUE_FILES],
254
+ rules: { ...vueRules({ indent: vueIndent }) }
255
+ }
256
+ ];
257
+ })());
184
258
  if (options.overrides) configs.push(Promise.resolve([{
185
259
  name: "qstanay/nuxt/overrides",
186
260
  files: [...SOURCE_FILES],
@@ -189,6 +263,6 @@ function nuxt(options = {}) {
189
263
  return configs;
190
264
  }
191
265
  //#endregion
192
- export { resolveEnabled as a, typescriptFiles as c, ignores as d, has as i, stylistic as l, vueRules as n, resolveStylisticOptions as o, VUE_FILES as r, sourceFiles as s, nuxt as t, sharedRules as u };
266
+ export { TYPESCRIPT_FILES as a, resolveEnabled as c, test as d, stylistic as f, ignores as h, typescript as i, resolveStylisticOptions as l, typescriptRules as m, vueRules as n, VUE_FILES as o, javascriptRules as p, loadTypescriptPlugin as r, has as s, nuxt as t, sourceFiles as u };
193
267
 
194
- //# sourceMappingURL=nuxt-jcrXAC4G.mjs.map
268
+ //# sourceMappingURL=nuxt-dkt3q_AQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nuxt-dkt3q_AQ.mjs","names":["stylisticConfig","testConfig"],"sources":["../src/configs/ignores.ts","../src/configs/shared-rules.ts","../src/configs/stylistic.ts","../src/configs/test.ts","../src/utils.ts","../src/configs/typescript.ts","../src/configs/vue-rules.ts","../src/nuxt.ts"],"sourcesContent":["import gitignore from 'eslint-config-flat-gitignore';\n\nimport type { TypedFlatConfigItem } from '../types';\n\nexport function ignores(extraIgnores: string[] = []): TypedFlatConfigItem[] {\n return [\n // Prefer project .gitignore when present; do not fail if missing.\n gitignore({ strict: false }),\n {\n name: 'qstanay/ignores',\n ignores: [\n '**/node_modules/**',\n '**/dist/**',\n '**/.output/**',\n '**/.nuxt/**',\n '**/.nitro/**',\n '**/coverage/**',\n ...extraIgnores,\n ],\n },\n ];\n}\n","import type { Linter } from 'eslint';\n\nexport function javascriptRules(): Linter.RulesRecord {\n return {\n 'no-console': ['error', { allow: ['warn', 'error'] }],\n 'no-debugger': 'error',\n 'eqeqeq': ['error', 'always'],\n 'object-shorthand': ['error', 'always'],\n 'prefer-template': 'error',\n 'curly': ['error', 'all'],\n 'arrow-body-style': ['error', 'as-needed'],\n };\n}\n\nexport function typescriptRules(options: { strict?: boolean } = {}): Linter.RulesRecord {\n const rules: Linter.RulesRecord = {\n // Disable base rules that conflict with TypeScript-aware variants.\n 'no-unused-vars': 'off',\n 'no-undef': 'off',\n\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n caughtErrorsIgnorePattern: '^_',\n destructuredArrayIgnorePattern: '^_',\n },\n ],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports', disallowTypeAnnotations: false },\n ],\n '@typescript-eslint/no-explicit-any': 'warn',\n '@typescript-eslint/no-non-null-assertion': 'error',\n '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],\n };\n\n if (options.strict) {\n rules['@typescript-eslint/no-magic-numbers'] = [\n 'warn',\n {\n ignore: [-1, 0, 1, 2, 1000],\n ignoreEnums: true,\n ignoreArrayIndexes: true,\n ignoreDefaultValues: true,\n ignoreClassFieldInitialValues: true,\n ignoreReadonlyClassProperties: true,\n },\n ];\n }\n\n return rules;\n}\n\n/**\n * Shared opinionated rules.\n * TypeScript rules are included only when `typescript` is true,\n * so JS-only projects do not reference a missing plugin.\n */\nexport function sharedRules(options: {\n typescript?: boolean;\n strict?: boolean;\n} = {}): Linter.RulesRecord {\n return {\n ...javascriptRules(),\n ...(options.typescript\n ? typescriptRules({ strict: options.strict })\n : {}),\n };\n}\n","import type { TypedFlatConfigItem, StylisticOptions } from '../types';\n\nasync function loadStylistic() {\n const mod = await import('@stylistic/eslint-plugin');\n return mod.default ?? mod;\n}\n\nexport async function stylistic(\n options: StylisticOptions = {},\n): Promise<TypedFlatConfigItem[]> {\n const {\n indent = 2,\n quotes = 'single',\n semi = true,\n maxLen = 100,\n } = options;\n\n const plugin = await loadStylistic();\n\n // `customize()` gives us a coherent bundle of stylistic rules.\n // Plugin name is fixed to `style` for stable rule IDs.\n const config = plugin.configs.customize({\n indent,\n quotes,\n semi,\n jsx: false,\n pluginName: 'style',\n }) as TypedFlatConfigItem;\n\n return [\n {\n name: 'qstanay/stylistic',\n plugins: {\n style: plugin,\n },\n rules: {\n ...(config.rules ?? {}),\n // Keep max line length aligned across all projects.\n 'style/max-len': [\n 'warn',\n {\n code: maxLen,\n ignoreUrls: true,\n ignoreStrings: true,\n ignoreTemplateLiterals: true,\n ignoreComments: true,\n ignoreTrailingComments: true,\n ignoreRegExpLiterals: true,\n },\n ],\n },\n },\n ];\n}\n","import type { TypedFlatConfigItem } from '../types';\n\nexport const TEST_FILES = [\n '**/__tests__/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n '**/*.{test,spec}.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n '**/tests/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n '**/test/**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n] as const;\n\n/**\n * Vitest layer: recommended rules + test globals for common test globs.\n */\nexport async function test(): Promise<TypedFlatConfigItem[]> {\n const mod = await import('@vitest/eslint-plugin');\n const vitest = (mod as any).default ?? mod;\n const globalsMod = await import('globals');\n const globals = (globalsMod as any).default ?? globalsMod;\n\n const recommended = vitest.configs?.recommended ?? {};\n const env = vitest.configs?.env ?? {};\n\n return [\n {\n name: 'qstanay/test',\n files: [...TEST_FILES],\n plugins: {\n vitest,\n },\n languageOptions: {\n globals: {\n ...globals.vitest,\n ...(env.languageOptions?.globals ?? {}),\n },\n },\n rules: {\n ...(recommended.rules ?? {}),\n },\n },\n ];\n}\n","import { isPackageExists } from 'local-pkg';\n\nimport type { StylisticOptions } from './types';\n\nexport function resolveEnabled(explicit: boolean | undefined, detected: boolean) {\n return explicit ?? detected;\n}\n\nexport function has(pkg: string) {\n return isPackageExists(pkg);\n}\n\nexport function resolveStylisticOptions(\n stylistic: boolean | StylisticOptions | undefined,\n): false | StylisticOptions {\n if (stylistic === false) {\n return false;\n }\n\n if (typeof stylistic === 'object') {\n return stylistic;\n }\n\n return {};\n}\n\nconst SCRIPT_FILES = [\n '**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n] as const;\n\nexport const VUE_FILES = ['**/*.vue'] as const;\n\n/** TypeScript sources — never includes `.vue` (Vue keeps its own root parser). */\nexport const TYPESCRIPT_FILES = [\n '**/*.{ts,tsx,cts,mts}',\n] as const;\n\n/**\n * Files that receive shared / override rule packs.\n * `.vue` is included only when Vue support is enabled.\n */\nexport function sourceFiles(vueEnabled: boolean): string[] {\n return vueEnabled\n ? [...SCRIPT_FILES, ...VUE_FILES]\n : [...SCRIPT_FILES];\n}\n\n/** Files that receive shared / override rule packs when Vue is enabled. */\nexport const SOURCE_FILES = [\n '**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts,vue}',\n] as const;\n","import { TYPESCRIPT_FILES, VUE_FILES } from '../utils';\n\nimport type { TypedFlatConfigItem } from '../types';\nimport type { Linter } from 'eslint';\n\nfunction collectRules(configs: TypedFlatConfigItem[]): Linter.RulesRecord {\n return Object.assign(\n {},\n ...configs\n .filter(config => config.rules)\n .map(config => config.rules),\n );\n}\n\nexport async function loadTypescriptPlugin() {\n const pluginMod = await import('@typescript-eslint/eslint-plugin');\n return (pluginMod as any).default ?? pluginMod;\n}\n\n/**\n * TypeScript layer.\n *\n * Important: `@typescript-eslint/parser` is never the root parser for `.vue`.\n * Vue SFCs keep `vue-eslint-parser` as root (from the Vue layer) and use the\n * TS parser only as a nested script parser.\n *\n * Flat config requires the plugin on every object that references\n * `@typescript-eslint/*` rules (see shared-rules / overrides too).\n */\nexport async function typescript(options: {\n vue?: boolean;\n} = {}): Promise<TypedFlatConfigItem[]> {\n const parserMod = await import('@typescript-eslint/parser');\n const plugin = await loadTypescriptPlugin();\n const parser = (parserMod as any).default ?? parserMod;\n\n const recommended = (plugin.configs?.['flat/recommended'] ?? []) as TypedFlatConfigItem[];\n const rules = collectRules(recommended);\n\n const configs: TypedFlatConfigItem[] = [\n {\n name: 'qstanay/typescript',\n files: [...TYPESCRIPT_FILES],\n languageOptions: {\n parser,\n parserOptions: {\n sourceType: 'module',\n ecmaVersion: 'latest',\n },\n },\n plugins: {\n '@typescript-eslint': plugin,\n },\n rules,\n },\n ];\n\n if (options.vue) {\n configs.push({\n name: 'qstanay/typescript/vue-rules',\n files: [...VUE_FILES],\n plugins: {\n '@typescript-eslint': plugin,\n },\n rules,\n });\n }\n\n return configs;\n}\n","import type { Linter } from 'eslint';\n\nexport function vueRules(options: {\n indent?: 2 | 4;\n} = {}): Linter.RulesRecord {\n const rules: Linter.RulesRecord = {\n 'vue/html-self-closing': [\n 'error',\n {\n html: {\n void: 'always',\n normal: 'always',\n component: 'always',\n },\n svg: 'always',\n math: 'always',\n },\n ],\n 'vue/attributes-order': 'error',\n 'vue/padding-line-between-tags': [\n 'error',\n [\n {\n blankLine: 'always',\n prev: '*',\n next: '*',\n },\n ],\n ],\n };\n\n if (options.indent !== undefined) {\n // Keep template indent in sync with @stylistic script indent.\n rules['vue/html-indent'] = ['error', options.indent];\n }\n\n return rules;\n}\n","import { ignores } from './configs/ignores';\nimport {\n javascriptRules,\n typescriptRules,\n} from './configs/shared-rules';\nimport { stylistic as stylisticConfig } from './configs/stylistic';\nimport { test as testConfig } from './configs/test';\nimport { loadTypescriptPlugin } from './configs/typescript';\nimport { vueRules } from './configs/vue-rules';\nimport {\n has,\n resolveEnabled,\n resolveStylisticOptions,\n SOURCE_FILES,\n TYPESCRIPT_FILES,\n VUE_FILES,\n} from './utils';\n\nimport type { StylisticOptions, TypedFlatConfigItem } from './types';\nimport type { Linter } from 'eslint';\n\nexport interface NuxtPresetOptions {\n stylistic?: boolean | StylisticOptions;\n /**\n * Enable stricter opinionated TypeScript rules (e.g. `no-magic-numbers`).\n */\n strict?: boolean;\n /**\n * Enable Vitest rules/globals for test files.\n * Auto-enabled when `vitest` is installed unless explicitly set.\n */\n test?: boolean;\n overrides?: Linter.RulesRecord;\n /**\n * Extra ignores appended to defaults / `.gitignore`.\n */\n ignores?: string[];\n}\n\n/**\n * Opinionated layer for Nuxt apps.\n *\n * Intended to be used with `@nuxt/eslint` + `withNuxt()`:\n * Nuxt already provides TypeScript/Vue/import plugins, so this preset\n * only adds stylistic formatting and shared opinionated rules.\n */\nexport function nuxt(options: NuxtPresetOptions = {}) {\n const stylisticOptions = resolveStylisticOptions(options.stylistic);\n const enableTest = resolveEnabled(options.test, has('vitest'));\n const vueIndent = stylisticOptions\n ? (stylisticOptions.indent ?? 2)\n : undefined;\n\n const configs: Promise<TypedFlatConfigItem[]>[] = [\n Promise.resolve(ignores(options.ignores)),\n ];\n\n if (stylisticOptions) {\n configs.push(stylisticConfig(stylisticOptions));\n }\n\n if (enableTest) {\n configs.push(testConfig());\n }\n\n configs.push((async () => {\n // Register plugin explicitly so the preset also works when tested standalone.\n // In real Nuxt apps `@nuxt/eslint` already provides it (duplicate name is fine).\n const plugin = await loadTypescriptPlugin();\n\n return [\n {\n name: 'qstanay/nuxt/shared-rules',\n files: [...SOURCE_FILES],\n rules: javascriptRules(),\n },\n {\n name: 'qstanay/nuxt/shared-rules/typescript',\n files: [...TYPESCRIPT_FILES, ...VUE_FILES],\n plugins: {\n '@typescript-eslint': plugin,\n },\n rules: typescriptRules({ strict: options.strict }),\n },\n {\n name: 'qstanay/nuxt/vue-rules',\n files: [...VUE_FILES],\n rules: {\n ...vueRules({ indent: vueIndent }),\n },\n },\n ];\n })());\n\n if (options.overrides) {\n // Dedicated trailing block so overrides always win over vue/shared rules.\n configs.push(Promise.resolve([\n {\n name: 'qstanay/nuxt/overrides',\n files: [...SOURCE_FILES],\n rules: options.overrides,\n },\n ]));\n }\n\n return configs;\n}\n"],"mappings":"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"}
package/dist/nuxt.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as nuxt, t as NuxtPresetOptions } from "./nuxt-B4vvMBlj.mjs";
1
+ import { n as nuxt, t as NuxtPresetOptions } from "./nuxt-Bxa1EDDq.mjs";
2
2
  export { NuxtPresetOptions, nuxt };
package/dist/nuxt.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as nuxt } from "./nuxt-jcrXAC4G.mjs";
1
+ import { t as nuxt } from "./nuxt-dkt3q_AQ.mjs";
2
2
  export { nuxt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qstanay/eslint-config",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Opinionated ESLint flat config for Nuxt, Vue and TypeScript",
5
5
  "type": "module",
6
6
  "exports": {
@@ -36,7 +36,8 @@
36
36
  "nuxt",
37
37
  "vue",
38
38
  "typescript",
39
- "stylistic"
39
+ "stylistic",
40
+ "vitest"
40
41
  ],
41
42
  "author": "Denis Lapshin",
42
43
  "license": "MIT",
@@ -53,11 +54,15 @@
53
54
  ],
54
55
  "peerDependencies": {
55
56
  "eslint": "^9.0.0 || ^10.0.0",
56
- "typescript": ">=4.9.0"
57
+ "typescript": ">=4.9.0",
58
+ "vitest": "*"
57
59
  },
58
60
  "peerDependenciesMeta": {
59
61
  "typescript": {
60
62
  "optional": true
63
+ },
64
+ "vitest": {
65
+ "optional": true
61
66
  }
62
67
  },
63
68
  "dependencies": {
@@ -66,6 +71,7 @@
66
71
  "@stylistic/eslint-plugin": "^5.10.0",
67
72
  "@typescript-eslint/eslint-plugin": "^8.0.0",
68
73
  "@typescript-eslint/parser": "^8.0.0",
74
+ "@vitest/eslint-plugin": "^1.6.26",
69
75
  "eslint-config-flat-gitignore": "^2.3.0",
70
76
  "eslint-flat-config-utils": "^3.2.0",
71
77
  "eslint-plugin-import-x": "^4.0.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"nuxt-B4vvMBlj.d.mts","names":[],"sources":["../src/types.ts","../src/nuxt.ts"],"mappings":";;KAEY,UAAU,KAAK,IAAI,QAAQ;KAE3B,sBAAsB,OAAO;UAExB;EACf;EACA;EACA;EACA;;UAGe;;;;EAIf;;;;EAIA;;;;EAIA;;;;;EAKA;EACA,sBAAsB;;;;EAItB;;;;EAIA,YAAY,OAAO;;;;UC9BJ;EACf,sBAAsB;;;;EAItB;EACA,YAAY,OAAO;;;;EAInB;;;;;;;;;iBAUc,KAAK,UAAS,oBAAsB,QAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"nuxt-jcrXAC4G.mjs","names":["stylisticConfig"],"sources":["../src/configs/ignores.ts","../src/configs/shared-rules.ts","../src/configs/stylistic.ts","../src/utils.ts","../src/configs/vue-rules.ts","../src/nuxt.ts"],"sourcesContent":["import gitignore from 'eslint-config-flat-gitignore';\n\nimport type { TypedFlatConfigItem } from '../types';\n\nexport function ignores(extraIgnores: string[] = []): TypedFlatConfigItem[] {\n return [\n // Prefer project .gitignore when present; do not fail if missing.\n gitignore({ strict: false }),\n {\n name: 'qstanay/ignores',\n ignores: [\n '**/node_modules/**',\n '**/dist/**',\n '**/.output/**',\n '**/.nuxt/**',\n '**/.nitro/**',\n '**/coverage/**',\n ...extraIgnores,\n ],\n },\n ];\n}\n","import type { Linter } from 'eslint';\n\nexport function javascriptRules(): Linter.RulesRecord {\n return {\n 'no-console': ['error', { allow: ['warn', 'error'] }],\n 'no-debugger': 'error',\n 'eqeqeq': ['error', 'always'],\n 'object-shorthand': ['error', 'always'],\n 'prefer-template': 'error',\n 'curly': ['error', 'all'],\n 'arrow-body-style': ['error', 'as-needed'],\n };\n}\n\nexport function typescriptRules(options: { strict?: boolean } = {}): Linter.RulesRecord {\n const rules: Linter.RulesRecord = {\n // Disable base rules that conflict with TypeScript-aware variants.\n 'no-unused-vars': 'off',\n 'no-undef': 'off',\n\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n caughtErrorsIgnorePattern: '^_',\n destructuredArrayIgnorePattern: '^_',\n },\n ],\n '@typescript-eslint/consistent-type-imports': [\n 'error',\n { prefer: 'type-imports', disallowTypeAnnotations: false },\n ],\n '@typescript-eslint/no-explicit-any': 'warn',\n '@typescript-eslint/no-non-null-assertion': 'error',\n '@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],\n };\n\n if (options.strict) {\n rules['@typescript-eslint/no-magic-numbers'] = [\n 'warn',\n {\n ignore: [-1, 0, 1, 2, 1000],\n ignoreEnums: true,\n ignoreArrayIndexes: true,\n ignoreDefaultValues: true,\n ignoreClassFieldInitialValues: true,\n ignoreReadonlyClassProperties: true,\n },\n ];\n }\n\n return rules;\n}\n\n/**\n * Shared opinionated rules.\n * TypeScript rules are included only when `typescript` is true,\n * so JS-only projects do not reference a missing plugin.\n */\nexport function sharedRules(options: {\n typescript?: boolean;\n strict?: boolean;\n} = {}): Linter.RulesRecord {\n return {\n ...javascriptRules(),\n ...(options.typescript\n ? typescriptRules({ strict: options.strict })\n : {}),\n };\n}\n","import type { TypedFlatConfigItem, StylisticOptions } from '../types';\n\nasync function loadStylistic() {\n const mod = await import('@stylistic/eslint-plugin');\n return mod.default ?? mod;\n}\n\nexport async function stylistic(\n options: StylisticOptions = {},\n): Promise<TypedFlatConfigItem[]> {\n const {\n indent = 2,\n quotes = 'single',\n semi = true,\n maxLen = 100,\n } = options;\n\n const plugin = await loadStylistic();\n\n // `customize()` gives us a coherent bundle of stylistic rules.\n // Plugin name is fixed to `style` for stable rule IDs.\n const config = plugin.configs.customize({\n indent,\n quotes,\n semi,\n jsx: false,\n pluginName: 'style',\n }) as TypedFlatConfigItem;\n\n return [\n {\n name: 'qstanay/stylistic',\n plugins: {\n style: plugin,\n },\n rules: {\n ...(config.rules ?? {}),\n // Keep max line length aligned across all projects.\n 'style/max-len': [\n 'warn',\n {\n code: maxLen,\n ignoreUrls: true,\n ignoreStrings: true,\n ignoreTemplateLiterals: true,\n ignoreComments: true,\n ignoreTrailingComments: true,\n ignoreRegExpLiterals: true,\n },\n ],\n },\n },\n ];\n}\n","import { isPackageExists } from 'local-pkg';\n\nimport type { StylisticOptions } from './types';\n\nexport function resolveEnabled(explicit: boolean | undefined, detected: boolean) {\n return explicit ?? detected;\n}\n\nexport function has(pkg: string) {\n return isPackageExists(pkg);\n}\n\nexport function resolveStylisticOptions(\n stylistic: boolean | StylisticOptions | undefined,\n): false | StylisticOptions {\n if (stylistic === false) {\n return false;\n }\n\n if (typeof stylistic === 'object') {\n return stylistic;\n }\n\n return {};\n}\n\nconst SCRIPT_FILES = [\n '**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts}',\n] as const;\n\nexport const VUE_FILES = ['**/*.vue'] as const;\n\n/** TypeScript sources (`.vue` is added only when Vue is enabled). */\nexport const TYPESCRIPT_FILES = [\n '**/*.{ts,tsx,cts,mts}',\n] as const;\n\n/**\n * Files that receive shared / override rule packs.\n * `.vue` is included only when Vue support is enabled.\n */\nexport function sourceFiles(vueEnabled: boolean): string[] {\n return vueEnabled\n ? [...SCRIPT_FILES, ...VUE_FILES]\n : [...SCRIPT_FILES];\n}\n\nexport function typescriptFiles(vueEnabled: boolean): string[] {\n return vueEnabled\n ? [...TYPESCRIPT_FILES, ...VUE_FILES]\n : [...TYPESCRIPT_FILES];\n}\n\n/** Files that receive shared / override rule packs when Vue is enabled. */\nexport const SOURCE_FILES = [\n '**/*.{js,mjs,cjs,jsx,ts,tsx,cts,mts,vue}',\n] as const;\n","import type { Linter } from 'eslint';\n\nexport function vueRules(): Linter.RulesRecord {\n return {\n 'vue/html-self-closing': [\n 'error',\n {\n html: {\n void: 'always',\n normal: 'always',\n component: 'always',\n },\n svg: 'always',\n math: 'always',\n },\n ],\n 'vue/attributes-order': 'error',\n 'vue/padding-line-between-tags': [\n 'error',\n [\n {\n blankLine: 'always',\n prev: '*',\n next: '*',\n },\n ],\n ],\n };\n}\n","import { ignores } from './configs/ignores';\nimport { sharedRules } from './configs/shared-rules';\nimport { stylistic as stylisticConfig } from './configs/stylistic';\nimport { vueRules } from './configs/vue-rules';\nimport { resolveStylisticOptions, SOURCE_FILES, VUE_FILES } from './utils';\n\nimport type { StylisticOptions, TypedFlatConfigItem } from './types';\nimport type { Linter } from 'eslint';\n\nexport interface NuxtPresetOptions {\n stylistic?: boolean | StylisticOptions;\n /**\n * Enable stricter opinionated TypeScript rules (e.g. `no-magic-numbers`).\n */\n strict?: boolean;\n overrides?: Linter.RulesRecord;\n /**\n * Extra ignores appended to defaults / `.gitignore`.\n */\n ignores?: string[];\n}\n\n/**\n * Opinionated layer for Nuxt apps.\n *\n * Intended to be used with `@nuxt/eslint` + `withNuxt()`:\n * Nuxt already provides TypeScript/Vue/import plugins, so this preset\n * only adds stylistic formatting and shared opinionated rules.\n */\nexport function nuxt(options: NuxtPresetOptions = {}) {\n const stylisticOptions = resolveStylisticOptions(options.stylistic);\n const configs: Promise<TypedFlatConfigItem[]>[] = [\n Promise.resolve(ignores(options.ignores)),\n ];\n\n if (stylisticOptions) {\n configs.push(stylisticConfig(stylisticOptions));\n }\n\n configs.push(Promise.resolve([\n {\n name: 'qstanay/nuxt/shared-rules',\n files: [...SOURCE_FILES],\n // Host (@nuxt/eslint) registers @typescript-eslint; include TS rules.\n rules: sharedRules({\n typescript: true,\n strict: options.strict,\n }),\n },\n {\n name: 'qstanay/nuxt/vue-rules',\n files: [...VUE_FILES],\n rules: {\n ...vueRules(),\n },\n },\n ]));\n\n if (options.overrides) {\n // Dedicated trailing block so overrides always win over vue/shared rules.\n configs.push(Promise.resolve([\n {\n name: 'qstanay/nuxt/overrides',\n files: [...SOURCE_FILES],\n rules: options.overrides,\n },\n ]));\n }\n\n return configs;\n}\n"],"mappings":"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"}