@santi020k/eslint-config-basic 2.0.0-beta.1 → 2.0.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
@@ -1,5 +1,213 @@
1
1
  # @santi020k/eslint-config-basic
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - **Breaking**: require ESLint 10. All packages now declare `"eslint": "^10.0.0"` as peer dependency (previously `^9.0.0 || ^10.0.0`), and `@santi020k/eslint-config-core` depends on `@eslint/js` v10.
8
+
9
+ ESLint v9.x reaches end-of-life on 2026-08-06; targeting v10 only lets the configs rely on v10 behavior:
10
+
11
+ - per-file config lookup (`eslint.config.*` resolved from each linted file's directory) — workspace packages can now ship their own config files alongside or instead of the root `projects` option
12
+ - JSX reference tracking — correct scope analysis for JSX without plugin workarounds
13
+ - the updated `eslint:recommended` baseline from `@eslint/js` v10
14
+
15
+ If you are still on ESLint 9, stay on the v1.x line of these packages.
16
+
17
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Lazy framework loading and normalized framework export naming.
18
+
19
+ **Lazy loading**: framework packages (angular-eslint, vue-eslint-parser, eslint-plugin-svelte, …) are now imported dynamically and only when their framework is enabled. Projects that enable none or few frameworks no longer pay the startup cost of loading every bundled framework plugin when `eslintConfig()` / `defineConfig()` runs.
20
+
21
+ **Breaking — normalized naming**: framework exports from `@santi020k/eslint-config-basic` now use bare framework names, and all of them are async factories returning `Promise<FlatConfigArray>`:
22
+
23
+ `angular`, `astro`, `expo`, `hono`, `lit`, `nest`, `next`, `nuxt`, `qwik`, `react`, `reactRouter`, `slidev`, `solid`, `svelte`, `tanstackStart`, `vite`, `vue` (plus deprecated `remix`).
24
+
25
+ The previous mixed-style names (`angularConfig`, `expoConfig`, `nestConfig`, `nextConfig`, `reactConfig`, `solidConfig`, `svelteConfig`, `vueConfig`) remain available as deprecated aliases of the same factories, but they are no longer plain config arrays — call them (and `await` the result) when composing manually:
26
+
27
+ ```js
28
+ // before (v1)
29
+ import { reactConfig } from "@santi020k/eslint-config-basic";
30
+
31
+ export default [...reactConfig];
32
+ ```
33
+
34
+ ```js
35
+ // after (v2)
36
+ import { react } from "@santi020k/eslint-config-basic";
37
+
38
+ export default [...(await react())];
39
+ ```
40
+
41
+ Most users are unaffected: `frameworks: { react: true }` and auto-detection behave exactly as before.
42
+
43
+ `@santi020k/eslint-config-core`: the `ImportedFramework` type now also accepts async factories (`(options?) => Promise<FlatConfigArray>`), so lazy factories can be passed directly as framework option values.
44
+
45
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Release v2 with a single public application install through `@santi020k/eslint-config-basic`.
46
+
47
+ Application projects no longer need to install or import separate framework config packages. Framework integrations are bundled behind the main package and can be enabled with booleans such as `frameworks.react: true`, `frameworks.next: true`, or by relying on auto-detection from `eslintConfig()`.
48
+
49
+ Detected framework configs are now enabled by default, while an explicit `frameworks: {}` remains the opt-out path. Next.js, Expo, and Remix automatically include React rules when enabled.
50
+
51
+ The documentation site now keeps the previous v1 docs under `/v1/`, updates the root docs for v2, and includes a v1 to v2 migration guide.
52
+
53
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Modernize the React stack and rename Remix:
54
+
55
+ - **BREAKING**: `@santi020k/eslint-config-react` now uses `@eslint-react/eslint-plugin` instead of `eslint-plugin-react` + `eslint-plugin-react-hooks`. Rule names change from `react/*` and `react-hooks/*` to `@eslint-react/*` (e.g. `react-hooks/exhaustive-deps` → `@eslint-react/exhaustive-deps`). This removes the `fixupConfigRules` compatibility shim, is ESLint 10-native, and includes the hooks rules. `react-compiler` and `react-refresh` plugins are unchanged.
56
+ - **BREAKING**: `@santi020k/eslint-config-remix` is now a deprecated alias that re-exports `@santi020k/eslint-config-react-router` (Remix merged into React Router v7). The `frameworks.remix` key still works but is deprecated — use `frameworks['react-router']`. The alias will be removed in the next major.
57
+ - The toolchain now builds with TypeScript 6.
58
+
59
+ ### Minor Changes
60
+
61
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add first-class support for Preact.
62
+
63
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - AI tooling workflow improvements:
64
+
65
+ - **Default ignores for AI artifacts**: the composed config now ignores AI coding-assistant folders (`.agent`, `.agents`, `.aider*`, `.claude`, `.clinerules`, `.codex`, `.copilot`, `.cursor`, `.gemini`, `.kiro`, `.opencode`, `.roo`, `.windsurf`) so generated agent rules are never linted as source code. Disable via `settings: [Setting.NoDefaultIgnores]`.
66
+ - **`generate-skill --check`**: CI mode that compares existing agent skill files (and guarded `AGENTS.md` / copilot-instructions sections) against freshly generated content without writing, and exits with code 1 when anything is stale or missing.
67
+ - **`generate-skill --create`**: scaffolds a root `AGENTS.md` with the guarded ESLint-standards section when the project has none.
68
+ - **`doctor` duplicate-ESLint detection**: warns when the project and the config packages resolve two different ESLint versions (e.g. an ESLint 9 project pulling in the config's ESLint 10 dependency). Both majors remain supported; the warning helps avoid editor/CLI rule-behavior drift.
69
+
70
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add broader AI tooling support:
71
+
72
+ - **New agent skill targets** in `generate-skill`: Gemini (`.gemini/styleguide.md`), Cline (`.clinerules/`), Roo Code (`.roo/rules/`), and Kiro (`.kiro/steering/` with `inclusion: always` front-matter). The generator also maintains a guarded ESLint-standards section in an existing root `AGENTS.md` (the open standard read by Codex CLI, OpenCode, Jules, Amp, and others), mirroring the `.github/copilot-instructions.md` behavior.
73
+ - **New library integrations**: `Library.Langchain` and `Library.LlamaIndex` add import safety rules for LangChain.js (`langchain`, `@langchain/*`) and LlamaIndex.TS (`llamaindex`, `@llamaindex/*`) projects, with auto-detection from `package.json` dependencies.
74
+ - Generated agent skills now report AI SDK, MCP, Mastra, OpenAI Agents, LangChain, and LlamaIndex integrations in the feature summary.
75
+
76
+ - [`dfba51d`](https://github.com/santi020k/eslint-config-basic/commit/dfba51dca7387a71a263af206eb86fd8df15f387) Thanks [@santi020k](https://github.com/santi020k)! - Improve config migration handling and enhance TypeScript options detection
77
+
78
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add optional `ignores` to `eslintConfig()` / `EslintConfigOptions` for extra global ignore globs, documented in the configuration guide.
79
+
80
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add `defineConfig` as a named alias for `eslintConfig`, keep implicit framework expansion from mutating caller-provided options, declare ESLint as a peer dependency of the umbrella package, and mark the package as side-effect free for bundlers.
81
+
82
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Fix `optionMergeStrategy: 'merge'` so explicitly passed options (`tools`, `testing`, `formats`, `libraries`, `extensions`, `frameworks`) are actually unioned with detected and preset values, as documented. Previously, providing any of these options silently replaced detected/preset values regardless of strategy. To fully opt out of detected frameworks, use `autoFrameworks: false` or `optionMergeStrategy: 'replace'` (an explicit `frameworks: {}` no longer opts out under the default merge strategy).
83
+
84
+ Other fixes:
85
+
86
+ - `detection: false` now also disables the default detected extensions (Unicorn, Perfectionist, Security); a new granular `detection: { extensions: false }` control is available.
87
+ - Negated glob patterns (`!pattern`) in `projects` sub-configs are now scoped correctly instead of producing invalid `path/!glob` patterns.
88
+ - Deprecate the unused `TsOptions` interface (`typescript: { project }` was ignored since v2) and correct stale doc comments on `ImportedFramework` and the no-op `Setting.Gitignore` / `Setting.DefaultIgnores` values.
89
+
90
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Disable context-sensitive framework rules that produce false positives in lint-staged and monorepo layouts: Nest's provider reference rule and Next's Pages Router link rule.
91
+
92
+ - [#91](https://github.com/santi020k/eslint-config-basic/pull/91) [`59158db`](https://github.com/santi020k/eslint-config-basic/commit/59158dbc0b48de041062f4d0c75b1f7e3a1e779c) Thanks [@santi020k](https://github.com/santi020k)! - Tailwind `noUnknownClasses` option, expanded testing config names, and virtual TS parser fix.
93
+
94
+ - **`TailwindOptions.noUnknownClasses`**: new optional field (`'error' | 'warn' | 'off' | false`) to control the `better-tailwindcss/no-unknown-classes` rule severity. Defaults to `'error'` when a Tailwind entry point is detected; set to `false` or `'off'` to disable.
95
+ - **Testing config overrides**: `TESTING_CONFIG_NAMES` now maps Cypress, Jest, JestDom, Playwright, TestingLibrary, and Vitest — so per-testing-tool file overrides are applied for all supported testing integrations, not just Playwright.
96
+ - **TypeScript virtual file parser**: the parser-setup config block now also covers virtual TS files (`parserSetupFiles` includes `GLOB_VIRTUAL_TS`), fixing missing parser options for framework-generated virtual modules.
97
+
98
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add new framework and integration support:
99
+
100
+ - **New framework packages**: Nuxt (`@nuxt/eslint-plugin` on top of the Vue config), Lit (`eslint-plugin-lit` + `eslint-plugin-wc`), React Router v7 (successor to Remix), and TanStack Start (bundles TanStack Router + Query rules). All are wired into `eslintConfig()` framework keys, bundled resolvers, auto-detection (`nuxt`, `lit`, `@react-router/dev`, `@tanstack/react-start`, `@tanstack/solid-start`), the CLI, and the agent-skill generator.
101
+ - **New formats**: `Format.Css` (official `@eslint/css` plugin) and `Format.Html` (`@html-eslint`).
102
+ - **New extensions**: `Extension.Node` (`eslint-plugin-n`, with TS-aware module-resolution overrides), `Extension.Compat` (browserslist compatibility), `Extension.DeMorgan`, `Extension.Depend`, and `Extension.Oxlint` (disables rules covered by Oxlint for hybrid setups, applied last like Biome).
103
+ - **New tool**: `Tool.Pnpm` (`eslint-plugin-pnpm`) enforcing pnpm catalogs and workspace settings in `package.json` and `pnpm-workspace.yaml`.
104
+
105
+ ### Patch Changes
106
+
107
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Refresh all external dependencies to their latest versions across the monorepo (58 bumps), including majors: TypeScript 6.0, Vite 8, Angular 22, MikroORM 7, and TypeORM 1.0. ESLint moves to 10.5, the `vite` and `eslint` pnpm overrides are updated to match, and the Angular and NestJS playgrounds now declare `rxjs@^7.8.2` explicitly so framework peer dependencies no longer resolve against a stale transitive rxjs 6.
108
+
109
+ - [#92](https://github.com/santi020k/eslint-config-basic/pull/92) [`9524bf8`](https://github.com/santi020k/eslint-config-basic/commit/9524bf8a955ece3cb44e8bdd4114bced0f8a646d) Thanks [@santi020k](https://github.com/santi020k)! - fix: export `boundaries` from the main package barrel — it was wired internally but missing from the public API, making `import { boundaries } from '@santi020k/eslint-config-basic'` resolve to undefined
110
+
111
+ - [#92](https://github.com/santi020k/eslint-config-basic/pull/92) [`9524bf8`](https://github.com/santi020k/eslint-config-basic/commit/9524bf8a955ece3cb44e8bdd4114bced0f8a646d) Thanks [@santi020k](https://github.com/santi020k)! - fix: gate `security/detect-non-literal-fs-filename` scripts override on Security extension being enabled — previously the rule was always emitted, which caused ESLint to throw "Could not find plugin 'security'" for consumers who did not opt in to the Security extension
112
+
113
+ - [#87](https://github.com/santi020k/eslint-config-basic/pull/87) [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc) Thanks [@santi020k](https://github.com/santi020k)! - Add `@santi020k/eslint-config-lite` as an optional composer package for projects that want to install framework and integration config packages manually. The full `@santi020k/eslint-config-basic` package remains the recommended default install.
114
+
115
+ Expose integration composition helpers from `@santi020k/eslint-config-integrations` so the lite package can lazy-load integration configs only when selected.
116
+
117
+ Teach `basic-eslint doctor` to recognize lite configs and warn when detected frameworks or integrations are missing their manually installed config packages.
118
+
119
+ Add `basic-eslint doctor --lite-install` to print the detected package-manager install command for switching a project to the lite package, including framework config packages, integrations, ESLint, and TypeScript when needed.
120
+
121
+ Document package-choice metrics, a Basic-to-Lite migration recipe, and a lite-specific `Preset.All` warning.
122
+
123
+ - Updated dependencies [[`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`dfba51d`](https://github.com/santi020k/eslint-config-basic/commit/dfba51dca7387a71a263af206eb86fd8df15f387), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`9524bf8`](https://github.com/santi020k/eslint-config-basic/commit/9524bf8a955ece3cb44e8bdd4114bced0f8a646d), [`59158db`](https://github.com/santi020k/eslint-config-basic/commit/59158dbc0b48de041062f4d0c75b1f7e3a1e779c), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`59158db`](https://github.com/santi020k/eslint-config-basic/commit/59158dbc0b48de041062f4d0c75b1f7e3a1e779c), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc), [`2ee503b`](https://github.com/santi020k/eslint-config-basic/commit/2ee503be55f5484624ee2e873fe59f348709eadc)]:
124
+ - @santi020k/eslint-config-preact@2.0.0
125
+ - @santi020k/eslint-config-core@2.0.0
126
+ - @santi020k/eslint-config-integrations@2.0.0
127
+ - @santi020k/eslint-config-angular@2.0.0
128
+ - @santi020k/eslint-config-astro@2.0.0
129
+ - @santi020k/eslint-config-expo@2.0.0
130
+ - @santi020k/eslint-config-hono@2.0.0
131
+ - @santi020k/eslint-config-lit@2.0.0
132
+ - @santi020k/eslint-config-nest@2.0.0
133
+ - @santi020k/eslint-config-next@2.0.0
134
+ - @santi020k/eslint-config-nuxt@2.0.0
135
+ - @santi020k/eslint-config-qwik@2.0.0
136
+ - @santi020k/eslint-config-react@2.0.0
137
+ - @santi020k/eslint-config-react-router@2.0.0
138
+ - @santi020k/eslint-config-remix@2.0.0
139
+ - @santi020k/eslint-config-slidev@2.0.0
140
+ - @santi020k/eslint-config-solid@2.0.0
141
+ - @santi020k/eslint-config-svelte@2.0.0
142
+ - @santi020k/eslint-config-tanstack-start@2.0.0
143
+ - @santi020k/eslint-config-typescript@2.0.0
144
+ - @santi020k/eslint-config-vite@2.0.0
145
+ - @santi020k/eslint-config-vue@2.0.0
146
+
147
+ ## 2.0.0-beta.3
148
+
149
+ ### Patch Changes
150
+
151
+ - Updated dependencies [[`af152cd`](https://github.com/santi020k/eslint-config-basic/commit/af152cda17961f9de8ca7bf069739f151fca65be)]:
152
+ - @santi020k/eslint-config-core@2.0.0-beta.3
153
+ - @santi020k/eslint-config-integrations@2.0.0-beta.3
154
+ - @santi020k/eslint-config-typescript@2.0.0-beta.3
155
+ - @santi020k/eslint-config-angular@2.0.0-beta.3
156
+ - @santi020k/eslint-config-astro@2.0.0-beta.3
157
+ - @santi020k/eslint-config-expo@2.0.0-beta.3
158
+ - @santi020k/eslint-config-hono@2.0.0-beta.3
159
+ - @santi020k/eslint-config-lit@2.0.0-beta.3
160
+ - @santi020k/eslint-config-nest@2.0.0-beta.3
161
+ - @santi020k/eslint-config-next@2.0.0-beta.3
162
+ - @santi020k/eslint-config-nuxt@2.0.0-beta.3
163
+ - @santi020k/eslint-config-qwik@2.0.0-beta.3
164
+ - @santi020k/eslint-config-react@2.0.0-beta.3
165
+ - @santi020k/eslint-config-react-router@2.0.0-beta.3
166
+ - @santi020k/eslint-config-remix@2.0.0-beta.3
167
+ - @santi020k/eslint-config-slidev@2.0.0-beta.3
168
+ - @santi020k/eslint-config-solid@2.0.0-beta.3
169
+ - @santi020k/eslint-config-svelte@2.0.0-beta.3
170
+ - @santi020k/eslint-config-tanstack-start@2.0.0-beta.3
171
+ - @santi020k/eslint-config-vite@2.0.0-beta.3
172
+ - @santi020k/eslint-config-vue@2.0.0-beta.3
173
+ - @santi020k/eslint-config-preact@2.0.0-beta.3
174
+
175
+ ## 2.0.0-beta.2
176
+
177
+ ### Minor Changes
178
+
179
+ - Tailwind `noUnknownClasses` option, expanded testing config names, and virtual TS parser fix.
180
+
181
+ - **`TailwindOptions.noUnknownClasses`**: new optional field (`'error' | 'warn' | 'off' | false`) to control the `better-tailwindcss/no-unknown-classes` rule severity. Defaults to `'error'` when a Tailwind entry point is detected; set to `false` or `'off'` to disable.
182
+ - **Testing config overrides**: `TESTING_CONFIG_NAMES` now maps Cypress, Jest, JestDom, Playwright, TestingLibrary, and Vitest — so per-testing-tool file overrides are applied for all supported testing integrations, not just Playwright.
183
+ - **TypeScript virtual file parser**: the parser-setup config block now also covers virtual TS files (`parserSetupFiles` includes `GLOB_VIRTUAL_TS`), fixing missing parser options for framework-generated virtual modules.
184
+
185
+ ### Patch Changes
186
+
187
+ - Updated dependencies []:
188
+ - @santi020k/eslint-config-core@2.0.0-beta.2
189
+ - @santi020k/eslint-config-typescript@2.0.0-beta.2
190
+ - @santi020k/eslint-config-integrations@2.0.0-beta.2
191
+ - @santi020k/eslint-config-angular@2.0.0-beta.2
192
+ - @santi020k/eslint-config-astro@2.0.0-beta.2
193
+ - @santi020k/eslint-config-expo@2.0.0-beta.2
194
+ - @santi020k/eslint-config-hono@2.0.0-beta.2
195
+ - @santi020k/eslint-config-lit@2.0.0-beta.2
196
+ - @santi020k/eslint-config-nest@2.0.0-beta.2
197
+ - @santi020k/eslint-config-next@2.0.0-beta.2
198
+ - @santi020k/eslint-config-nuxt@2.0.0-beta.2
199
+ - @santi020k/eslint-config-qwik@2.0.0-beta.2
200
+ - @santi020k/eslint-config-react@2.0.0-beta.2
201
+ - @santi020k/eslint-config-react-router@2.0.0-beta.2
202
+ - @santi020k/eslint-config-remix@2.0.0-beta.2
203
+ - @santi020k/eslint-config-slidev@2.0.0-beta.2
204
+ - @santi020k/eslint-config-solid@2.0.0-beta.2
205
+ - @santi020k/eslint-config-svelte@2.0.0-beta.2
206
+ - @santi020k/eslint-config-tanstack-start@2.0.0-beta.2
207
+ - @santi020k/eslint-config-vite@2.0.0-beta.2
208
+ - @santi020k/eslint-config-vue@2.0.0-beta.2
209
+ - @santi020k/eslint-config-preact@2.0.0-beta.2
210
+
3
211
  ## 2.0.0-beta.1
4
212
 
5
213
  ### Minor Changes
@@ -1,4 +1,7 @@
1
- import { loadModule } from "./lazy.js";
1
+ import {
2
+ createModuleLoader
3
+ } from "@santi020k/eslint-config-core";
4
+ const loadModule = createModuleLoader((specifier) => import.meta.resolve(specifier, import.meta.url));
2
5
  const createDetectedFrameworkFlags = (detectedFrameworks = []) => Object.fromEntries(
3
6
  detectedFrameworks.map((framework) => [framework, true])
4
7
  );
package/dist/index.d.ts CHANGED
@@ -1,12 +1,10 @@
1
- import { EslintConfigOptions, FlatConfigArray } from '@santi020k/eslint-config-core';
1
+ import { EslintConfigOptions, ConfigInput, FlatConfigArray } from '@santi020k/eslint-config-core';
2
2
  export { DetectedFrameworkName, DetectionOptions, EslintConfigOptions, Extension, ExtensionName, ExtensionOption, FlatConfigArray, Format, FormatName, FormatOption, ImportGroupOptions, ImportedFramework, Library, LibraryName, LibraryOption, NextMode, NextModeName, NextModeOption, OptionalConfigMap, OptionalConfigName, Preset, PresetName, PresetOption, ReactConfigKeys, Runtime, RuntimeName, RuntimeOption, Setting, SettingName, SettingOption, StrictMode, Testing, TestingName, TestingOption, Tool, ToolName, ToolOption, TypeScriptMode, TypeScriptOptions, coreConfig, createCoreConfig, createImportGroups, detectProjectOptions, getGlobalsForRuntime, gitignore, groups, hasReactConfig, jsConfig } from '@santi020k/eslint-config-core';
3
- import { TSESLint } from '@typescript-eslint/utils';
4
3
  export { AGENT_TARGETS, AgentTarget, EslintConfigFeatures, GenerateSkillOptions, GenerateSkillResult, generateAgentSkills, generateSkillContent } from './agent-skill-generator.js';
5
4
  export { angular, angularConfig, astro, expo, expoConfig, hono, lit, nest, nestConfig, next, nextConfig, nuxt, preact, preactConfig, qwik, react, reactConfig, reactRouter, remix, slidev, solid, solidConfig, svelte, svelteConfig, tanstackStart, vite, vue, vueConfig } from './frameworks.js';
6
- export { a11y, aiSdk, autogen, bestPractices, biome, command, compat, cspell, css, cypress, deMorgan, depend, docker, drizzle, githubActions, googleGenAi, graphql, html, i18next, jest, jestDom, jsdoc, jsonc, langchain, llamaIndex, markdown, mastra, mcp, mdx, mikroOrm, noOnlyTests, node, nx, openAiAgents, oxlint, packageJson, perfectionist, playwright, pnpm, prettier, prisma, regexp, security, sequelize, sonarjs, stencil, storybook, swagger, tailwind, tanstackQuery, tanstackRouter, testingLibrary, toml, turbo, typeorm, unicorn, vitest, yaml, zod } from '@santi020k/eslint-config-integrations';
5
+ export { a11y, aiSdk, autogen, bestPractices, biome, boundaries, command, compat, cspell, css, cypress, deMorgan, depend, docker, drizzle, githubActions, googleGenAi, graphql, html, i18next, jest, jestDom, jsdoc, jsonc, langchain, llamaIndex, markdown, mastra, mcp, mdx, mikroOrm, noOnlyTests, node, nx, openAiAgents, oxlint, packageJson, perfectionist, playwright, pnpm, prettier, prisma, regexp, security, sequelize, sonarjs, stencil, storybook, swagger, tailwind, tanstackQuery, tanstackRouter, testingLibrary, toml, turbo, typeorm, unicorn, vitest, yaml, zod } from '@santi020k/eslint-config-integrations';
7
6
  export { tsConfig, typescriptConfig } from '@santi020k/eslint-config-typescript';
8
7
 
9
- type ConfigInput = false | null | TSESLint.FlatConfig.Config | TSESLint.FlatConfig.ConfigArray | undefined;
10
8
  /**
11
9
  * Generates the ESLint configuration array, applying configurations
12
10
  * and integration settings based on the input configuration.
package/dist/index.js CHANGED
@@ -1,22 +1,33 @@
1
- import { existsSync } from "node:fs";
2
1
  import { join } from "node:path";
3
2
  import {
3
+ applyDetectionControls,
4
+ applyStrictMode,
5
+ applyStrictProfileDefaults,
4
6
  coreConfig,
5
7
  createCoreConfig,
8
+ DEFAULT_IGNORES,
6
9
  detectProjectOptions,
7
10
  Extension,
8
- Format,
11
+ findTailwindEntryPoint,
12
+ flattenConfigInputs,
13
+ GENERATED_CODE_IGNORES,
14
+ getStrictMode,
9
15
  gitignore,
10
16
  Library,
17
+ mergeFrameworkOption,
18
+ mergeOptionalBucket,
11
19
  NextMode,
20
+ patchImportGroups,
12
21
  Preset,
22
+ resolveTsconfigRootDir,
23
+ resolveTypescriptOptions,
13
24
  Runtime,
25
+ scopeConfigToProject,
14
26
  Setting,
15
- Testing,
16
- Tool
27
+ Testing
17
28
  } from "@santi020k/eslint-config-core";
29
+ import { tailwind as tailwindIntegration } from "@santi020k/eslint-config-integrations";
18
30
  import { createTypescriptConfig } from "@santi020k/eslint-config-typescript";
19
- import { applyStrictMode } from "./compose.js";
20
31
  import { createDetectedFrameworkFlags } from "./frameworks.js";
21
32
  import { getIntegrationConfigs, getPrettierConfig } from "./integrations.js";
22
33
  import { resolveFramework, resolvePreset } from "./resolvers.js";
@@ -61,7 +72,7 @@ import {
61
72
  createImportGroups,
62
73
  detectProjectOptions as detectProjectOptions2,
63
74
  Extension as Extension2,
64
- Format as Format2,
75
+ Format,
65
76
  getGlobalsForRuntime,
66
77
  gitignore as gitignore2,
67
78
  groups,
@@ -74,7 +85,7 @@ import {
74
85
  Runtime as Runtime2,
75
86
  Setting as Setting2,
76
87
  Testing as Testing2,
77
- Tool as Tool2
88
+ Tool
78
89
  } from "@santi020k/eslint-config-core";
79
90
  import {
80
91
  a11y,
@@ -82,6 +93,7 @@ import {
82
93
  autogen,
83
94
  bestPractices,
84
95
  biome,
96
+ boundaries,
85
97
  command,
86
98
  compat,
87
99
  cspell,
@@ -138,270 +150,54 @@ import {
138
150
  zod
139
151
  } from "@santi020k/eslint-config-integrations";
140
152
  import { tsConfig, typescriptConfig } from "@santi020k/eslint-config-typescript";
141
- const toUniqueArray = (values) => [...new Set(values)];
142
- const DEFAULT_IGNORES = [
143
- "**/dist/**",
144
- "**/build/**",
145
- "**/coverage/**",
146
- "**/.turbo/**",
147
- "**/.next/**",
148
- "**/.open-next/**",
149
- "**/.astro/**",
150
- "**/.svelte-kit/**",
151
- "**/.vercel/**",
152
- "**/.wrangler/**",
153
- "**/playwright-report/**",
154
- "**/test-results/**",
155
- "**/node_modules/**",
156
- "**/tsconfig.tsbuildinfo",
157
- // Package manager lock files (machine-generated, slow to lint)
158
- "**/pnpm-lock.yaml",
159
- "**/yarn.lock",
160
- "**/package-lock.json",
161
- "**/bun.lock",
162
- "**/bun.lockb",
163
- // AI coding-assistant artifacts (rule/skill folders managed by tools, not source code)
164
- "**/.agent/**",
165
- "**/.agents/**",
166
- "**/.aider*",
167
- "**/.aider*/**",
168
- "**/.claude/**",
169
- "**/.clinerules/**",
170
- "**/.codex/**",
171
- "**/.copilot/**",
172
- "**/.cursor/**",
173
- "**/.gemini/**",
174
- "**/.kiro/**",
175
- "**/.opencode/**",
176
- "**/.roo/**",
177
- "**/.windsurf/**"
178
- ];
179
- const GENERATED_CODE_IGNORES = [
180
- "**/__generated__/**",
181
- "**/generated/**",
182
- "**/codegen/**",
183
- "**/*.generated.*",
184
- "**/*.gen.*",
185
- "**/graphql.ts",
186
- "**/graphql.tsx",
187
- "**/gql/**",
188
- "**/.prisma/**"
189
- ];
190
- const TAILWIND_ENTRYPOINT_CANDIDATES = [
191
- "src/styles/global.css",
192
- "src/app/globals.css",
193
- "src/globals.css",
194
- "src/index.css",
195
- "app/globals.css",
196
- "styles/global.css"
197
- ];
198
- const OPTIONAL_BUCKETS = {
199
- extensions: Object.values(Extension),
200
- formats: Object.values(Format),
201
- libraries: Object.values(Library),
202
- testing: Object.values(Testing),
203
- tools: Object.values(Tool)
204
- };
205
- const flattenConfigInputs = (configs) => configs.flatMap((config) => {
206
- if (!config) return [];
207
- return Array.isArray(config) ? config : [config];
208
- });
209
- const mergeArrayOption = (detectedValues, presetValues, explicitValues, strategy) => {
210
- if (strategy === "replace") {
211
- if (explicitValues) return toUniqueArray(explicitValues);
212
- if (presetValues) return toUniqueArray(presetValues);
213
- return toUniqueArray(detectedValues);
214
- }
215
- return toUniqueArray([
216
- ...detectedValues,
217
- ...presetValues ?? [],
218
- ...explicitValues ?? []
219
- ]);
220
- };
221
- const isOptionalBucketValue = (bucket, value) => OPTIONAL_BUCKETS[bucket].includes(value);
222
- const getFeatureEntries = (options, bucket, enabled) => [
223
- ...Object.entries(options?.features ?? {}),
224
- ...Object.entries(options?.integrations ?? {})
225
- ].filter(([name, value]) => value === enabled && isOptionalBucketValue(bucket, name)).map(([name]) => name);
226
- const applyFeatureDisables = (values, options, bucket) => {
227
- const disabled = new Set(getFeatureEntries(options, bucket, false));
228
- return values.filter((value) => !disabled.has(value));
229
- };
230
- const mergeOptionalBucket = (bucket, detectedValues, presetValues, explicitValues, options, strategy) => {
231
- const featureEntries = getFeatureEntries(options, bucket, true);
232
- const combined = explicitValues !== void 0 || featureEntries.length > 0 ? [...explicitValues ?? [], ...featureEntries] : void 0;
233
- return applyFeatureDisables(
234
- mergeArrayOption(detectedValues, presetValues, combined, strategy),
235
- options,
236
- bucket
237
- );
238
- };
239
- const mergeFrameworkOption = (detectedFrameworks, presetFrameworks, explicitFrameworks, strategy) => {
240
- if (strategy === "replace") {
241
- if (explicitFrameworks) return { ...explicitFrameworks };
242
- if (presetFrameworks) return { ...presetFrameworks };
243
- return { ...detectedFrameworks };
244
- }
245
- return {
246
- ...detectedFrameworks,
247
- ...presetFrameworks ?? {},
248
- ...explicitFrameworks ?? {}
249
- };
250
- };
251
- const resolveDetectionOptions = (detection, defaults = {}) => {
252
- const defaultControls = {
253
- extensions: true,
254
- formats: true,
255
- frameworks: true,
256
- libraries: true,
257
- nextMode: true,
258
- projects: false,
259
- runtime: true,
260
- testing: true,
261
- tools: true,
262
- typescript: true,
263
- ...defaults
264
- };
265
- if (detection === false) {
266
- return Object.fromEntries(
267
- Object.keys(defaultControls).map((key) => [key, false])
268
- );
269
- }
270
- if (detection === true || detection === void 0) {
271
- return defaultControls;
272
- }
273
- return {
274
- ...defaultControls,
275
- ...detection
276
- };
277
- };
278
- const applyArrayControls = (controls, detected) => ({
279
- detectedFrameworks: controls.frameworks ? detected.detectedFrameworks : [],
280
- extensions: controls.extensions ? detected.extensions : [],
281
- formats: controls.formats ? detected.formats : [],
282
- libraries: controls.libraries ? detected.libraries : [],
283
- testing: controls.testing ? detected.testing : [],
284
- tools: controls.tools ? detected.tools : []
285
- });
286
- const applyScalarControls = (controls, detected) => ({
287
- nextMode: controls.nextMode ? detected.nextMode : void 0,
288
- preset: controls.typescript && controls.runtime ? detected.preset : void 0,
289
- projects: controls.projects ? detected.projects : void 0,
290
- runtime: controls.runtime ? detected.runtime : void 0,
291
- typescript: controls.typescript ? detected.typescript : false
292
- });
293
- const applyDetectionControls = (detected, detection, defaults) => {
294
- const controls = resolveDetectionOptions(detection, defaults);
295
- return { ...detected, ...applyArrayControls(controls, detected), ...applyScalarControls(controls, detected) };
296
- };
297
- const getStrictMode = (explicitStrict, presetStrict) => explicitStrict ?? presetStrict ?? false;
298
- const applyStrictProfileDefaults = (extensions, strict) => {
299
- if (strict !== "pedantic") return extensions;
300
- return toUniqueArray([...extensions, Extension.BestPractices]);
301
- };
302
- const hasTsconfig = (rootDir) => [
303
- "tsconfig.eslint.json",
304
- "tsconfig.json",
305
- "tsconfig.base.json",
306
- "tsconfig.app.json",
307
- "tsconfig.node.json"
308
- ].some((fileName) => existsSync(join(rootDir, fileName)));
309
- const resolveTypescriptOptions = (typescript) => {
310
- if (!typescript || typescript === "off") return false;
311
- if (typescript === true) return { mode: "type-aware" };
312
- if (typeof typescript === "string") return { mode: typescript };
313
- if (typescript.mode === "off") return false;
314
- return {
315
- ...typescript,
316
- mode: typescript.mode ?? "type-aware"
317
- };
318
- };
319
- const resolveTsconfigRootDir = (rootDir, typescript, explicitRootDir) => {
320
- if (explicitRootDir) return explicitRootDir;
321
- return resolveTypescriptOptions(typescript) && hasTsconfig(rootDir) ? rootDir : void 0;
322
- };
323
- const findTailwindEntryPoint = (rootDir) => TAILWIND_ENTRYPOINT_CANDIDATES.find(
324
- (candidate) => existsSync(join(rootDir, candidate))
325
- );
326
153
  const buildTailwindResult = (options, entryPoint) => ({
327
154
  detectComponentClasses: options.detectComponentClasses ?? true,
328
155
  ...entryPoint ? { entryPoint } : {},
329
- ...options.ignore?.length ? { ignore: options.ignore } : {}
156
+ ...options.ignore?.length ? { ignore: options.ignore } : {},
157
+ ...options.noUnknownClasses === void 0 ? {} : { noUnknownClasses: options.noUnknownClasses }
330
158
  });
331
159
  const resolveTailwindOptions = (rootDir, tailwind2) => {
332
160
  if (tailwind2 === false) return void 0;
333
161
  const options = tailwind2 ?? {};
334
162
  const entryPoint = options.entryPoint ?? findTailwindEntryPoint(rootDir);
335
- if (!entryPoint && options.detectComponentClasses === void 0 && !options.ignore?.length) return void 0;
163
+ if (!entryPoint && options.detectComponentClasses === void 0 && !options.ignore?.length && options.noUnknownClasses === void 0) return void 0;
336
164
  return buildTailwindResult(options, entryPoint);
337
165
  };
338
- const createTailwindSettingsConfig = (tailwindOptions) => {
166
+ const createNoUnknownClassesRule = (tailwindOptions, unknownClassOptions, hasUnknownClassOptions) => {
167
+ if (!hasUnknownClassOptions && tailwindOptions.noUnknownClasses === void 0) return void 0;
168
+ const severity = tailwindOptions.noUnknownClasses ?? "error";
169
+ if (severity === false) return "off";
170
+ return hasUnknownClassOptions ? [severity, unknownClassOptions] : severity;
171
+ };
172
+ const loadTailwindPlugins = async () => {
173
+ const tailwindConfigs = await tailwindIntegration();
174
+ const pluginConfig = tailwindConfigs.find((config) => config.plugins?.["better-tailwindcss"]);
175
+ return pluginConfig?.plugins ?? {};
176
+ };
177
+ const createTailwindSettingsConfig = async (tailwindOptions) => {
178
+ const { noUnknownClasses: _noUnknownClasses, ...settingsOptions } = tailwindOptions;
339
179
  const unknownClassOptions = {
340
180
  ...tailwindOptions.entryPoint ? { entryPoint: tailwindOptions.entryPoint } : {},
341
181
  ...tailwindOptions.ignore?.length ? { ignore: tailwindOptions.ignore } : {}
342
182
  };
343
183
  const hasUnknownClassOptions = Object.keys(unknownClassOptions).length > 0;
184
+ const noUnknownClassesRule = createNoUnknownClassesRule(tailwindOptions, unknownClassOptions, hasUnknownClassOptions);
344
185
  return {
345
186
  name: "eslint-config-basic/tailwind-settings",
346
- ...hasUnknownClassOptions ? { rules: { "better-tailwindcss/no-unknown-classes": ["error", unknownClassOptions] } } : {},
187
+ plugins: await loadTailwindPlugins(),
188
+ ...noUnknownClassesRule === void 0 ? {} : { rules: { "better-tailwindcss/no-unknown-classes": noUnknownClassesRule } },
347
189
  settings: {
348
- "better-tailwindcss": tailwindOptions
349
- }
350
- };
351
- };
352
- const scopeFilePattern = (projectPath, pattern) => {
353
- if (typeof pattern === "string") {
354
- const isNegated = pattern.startsWith("!");
355
- const basePattern = (isNegated ? pattern.slice(1) : pattern).replace(/^\.\//, "");
356
- const scoped = `${projectPath.replace(/\/$/, "")}/${basePattern}`;
357
- return isNegated ? `!${scoped}` : scoped;
358
- }
359
- if (Array.isArray(pattern)) {
360
- return pattern.map((item) => scopeFilePattern(projectPath, item));
361
- }
362
- return pattern;
363
- };
364
- const scopeConfigToProject = (config, projectPath) => {
365
- if ("ignores" in config && !config.files) {
366
- return {
367
- ...config,
368
- ignores: config.ignores?.map((pattern) => scopeFilePattern(projectPath, pattern))
369
- };
370
- }
371
- return {
372
- ...config,
373
- files: Array.isArray(config.files) ? config.files.map((pattern) => scopeFilePattern(projectPath, pattern)) : [`${projectPath.replace(/\/$/, "")}/**/*`]
374
- };
375
- };
376
- const patchImportGroupsConfig = (config, workspacePatterns) => {
377
- const rule = config.rules?.["simple-import-sort/imports"];
378
- if (!Array.isArray(rule) || !rule[1]) return config;
379
- const ruleOpts = rule[1];
380
- const existingGroups = ruleOpts.groups ?? [];
381
- if (existingGroups.length === 0) return config;
382
- const externalIdx = existingGroups.findIndex((g) => g.some((p) => p.includes("^@?\\w")));
383
- const insertAt = externalIdx >= 0 ? externalIdx : existingGroups.length;
384
- return {
385
- ...config,
386
- rules: {
387
- ...config.rules,
388
- "simple-import-sort/imports": [
389
- rule[0],
390
- { ...ruleOpts, groups: [...existingGroups.slice(0, insertAt), workspacePatterns, ...existingGroups.slice(insertAt)] }
391
- ]
190
+ "better-tailwindcss": settingsOptions
392
191
  }
393
192
  };
394
193
  };
395
- const patchImportGroups = (allConfigs, workspacePrefixes) => {
396
- const workspacePatterns = workspacePrefixes.map(
397
- (p) => `^${p.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")}(/.*|$)`
398
- );
399
- return allConfigs.map(
400
- (item) => Array.isArray(item) ? item : patchImportGroupsConfig(item, workspacePatterns)
401
- );
402
- };
403
194
  const TESTING_CONFIG_NAMES = {
404
- [Testing.Playwright]: ["integrations/playwright"]
195
+ [Testing.Cypress]: ["integrations/cypress"],
196
+ [Testing.Jest]: ["integrations/jest"],
197
+ [Testing.JestDom]: ["eslint-config-integrations/jest-dom"],
198
+ [Testing.Playwright]: ["integrations/playwright"],
199
+ [Testing.TestingLibrary]: ["integrations/testing-library"],
200
+ [Testing.Vitest]: ["integrations/vitest"]
405
201
  };
406
202
  const applyTestingFileOverrides = (configs, testingFiles) => {
407
203
  if (!testingFiles) return configs;
@@ -473,6 +269,23 @@ const buildIgnoresConfig = (useDefaultIgnores, useGeneratedCodeIgnores, useGitig
473
269
  name: "eslint-config-basic/ignores"
474
270
  }] : []
475
271
  });
272
+ const FRAMEWORK_EXTRA_OPTS = {
273
+ astro: (ctx) => ({ hasReact: ctx.hasReact, hasSolid: ctx.hasSolid, hasSvelte: ctx.hasSvelte, hasVue: ctx.hasVue }),
274
+ hono: (ctx) => ({ runtime: ctx.runtime }),
275
+ slidev: (ctx) => ({ runtime: ctx.runtime }),
276
+ vite: (ctx) => ({ runtime: ctx.runtime })
277
+ };
278
+ const resolveEnabledFrameworks = async (frameworks, ctx) => {
279
+ const entries = await Promise.all(
280
+ Object.entries(frameworks).filter((entry) => Boolean(entry[1])).map(
281
+ ([name, value]) => (
282
+ // eslint-disable-next-line security/detect-object-injection
283
+ resolveFramework(name, value, FRAMEWORK_EXTRA_OPTS[name]?.(ctx)).then((config) => [name, config])
284
+ )
285
+ )
286
+ );
287
+ return Object.fromEntries(entries);
288
+ };
476
289
  const buildEslintConfigs = async (params) => {
477
290
  const {
478
291
  astroOptions,
@@ -495,47 +308,8 @@ const buildEslintConfigs = async (params) => {
495
308
  userIgnores
496
309
  } = params;
497
310
  const { hasReact, hasSolid, hasSvelte, hasVue } = astroOptions;
498
- const [
499
- reactParam,
500
- nextParam,
501
- astroParam,
502
- expoParam,
503
- nestParam,
504
- honoParam,
505
- vueParam,
506
- svelteParam,
507
- solidParam,
508
- angularParam,
509
- qwikParam,
510
- remixParam,
511
- reactRouterParam,
512
- tanstackStartParam,
513
- nuxtParam,
514
- preactParam,
515
- litParam,
516
- slidevParam,
517
- viteParam
518
- ] = await Promise.all([
519
- resolveFramework("react", resolvedFrameworks.react),
520
- resolveFramework("next", resolvedFrameworks.next),
521
- resolveFramework("astro", resolvedFrameworks.astro, { hasReact, hasSolid, hasSvelte, hasVue }),
522
- resolveFramework("expo", resolvedFrameworks.expo),
523
- resolveFramework("nest", resolvedFrameworks.nest),
524
- resolveFramework("hono", resolvedFrameworks.hono, { runtime }),
525
- resolveFramework("vue", resolvedFrameworks.vue),
526
- resolveFramework("svelte", resolvedFrameworks.svelte),
527
- resolveFramework("solid", resolvedFrameworks.solid),
528
- resolveFramework("angular", resolvedFrameworks.angular),
529
- resolveFramework("qwik", resolvedFrameworks.qwik),
530
- resolveFramework("remix", resolvedFrameworks.remix),
531
- resolveFramework("react-router", resolvedFrameworks["react-router"]),
532
- resolveFramework("tanstack-start", resolvedFrameworks["tanstack-start"]),
533
- resolveFramework("nuxt", resolvedFrameworks.nuxt),
534
- resolveFramework("preact", resolvedFrameworks.preact),
535
- resolveFramework("lit", resolvedFrameworks.lit),
536
- resolveFramework("slidev", resolvedFrameworks.slidev, { runtime }),
537
- resolveFramework("vite", resolvedFrameworks.vite, { runtime })
538
- ]);
311
+ const fw = await resolveEnabledFrameworks(resolvedFrameworks, { hasReact, hasSolid, hasSvelte, hasVue, runtime });
312
+ const get = (name) => fw[name] ?? [];
539
313
  return [
540
314
  ...defaultIgnores,
541
315
  ...userIgnores,
@@ -559,29 +333,29 @@ const buildEslintConfigs = async (params) => {
559
333
  name: "eslint-config-basic/commonjs"
560
334
  },
561
335
  ...runtimeCoreConfig,
562
- ...hasReact ? reactParam : [],
563
- ...nextParam,
564
- ...expoParam,
565
- ...nestParam,
566
- ...honoParam,
567
- ...vueParam,
568
- ...svelteParam,
569
- ...solidParam,
570
- ...angularParam,
571
- ...qwikParam,
572
- ...remixParam,
573
- ...reactRouterParam,
574
- ...tanstackStartParam,
575
- ...nuxtParam,
576
- ...preactParam,
577
- ...litParam,
578
- ...slidevParam,
579
- ...viteParam,
336
+ ...hasReact ? get("react") : [],
337
+ ...get("next"),
338
+ ...get("expo"),
339
+ ...get("nest"),
340
+ ...get("hono"),
341
+ ...get("vue"),
342
+ ...get("svelte"),
343
+ ...get("solid"),
344
+ ...get("angular"),
345
+ ...get("qwik"),
346
+ ...get("remix"),
347
+ ...get("react-router"),
348
+ ...get("tanstack-start"),
349
+ ...get("nuxt"),
350
+ ...get("preact"),
351
+ ...get("lit"),
352
+ ...get("slidev"),
353
+ ...get("vite"),
580
354
  ...resolvedTypescript ? createTypescriptConfig({
581
355
  ...resolvedTypescript,
582
356
  tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
583
357
  }) : [],
584
- ...astroParam,
358
+ ...get("astro"),
585
359
  ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [{
586
360
  files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
587
361
  name: "eslint-config-next/app-router-overrides",
@@ -591,11 +365,34 @@ const buildEslintConfigs = async (params) => {
591
365
  await getIntegrationConfigs(uniqueLibraries, uniqueTools, uniqueTesting, uniqueFormats, uniqueExtensions),
592
366
  testingFiles
593
367
  ),
594
- ...tailwindOptions ? [createTailwindSettingsConfig(tailwindOptions)] : [],
368
+ {
369
+ files: ["**/scripts/**/*.{js,mjs,cjs,ts,mts,cts}"],
370
+ name: "eslint-config-basic/scripts-overrides",
371
+ rules: {
372
+ "n/no-unpublished-import": "off",
373
+ ...uniqueExtensions.includes(Extension.Security) ? { "security/detect-non-literal-fs-filename": "off" } : {}
374
+ }
375
+ },
376
+ ...tailwindOptions ? [await createTailwindSettingsConfig(tailwindOptions)] : [],
595
377
  ...await getPrettierConfig(uniqueTools)
596
378
  ];
597
379
  };
598
- const logEslintDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStrategy, preset, resolvedFrameworks, resolvedTypescript, runtime, tsconfigRootDir, uniqueExtensions, uniqueFormats, uniqueLibraries, uniqueTesting, uniqueTools) => {
380
+ const logEslintDebug = ({
381
+ autoFrameworks,
382
+ detectRootDir,
383
+ nextMode,
384
+ optionMergeStrategy,
385
+ preset,
386
+ resolvedFrameworks,
387
+ resolvedTypescript,
388
+ runtime,
389
+ tsconfigRootDir,
390
+ uniqueExtensions,
391
+ uniqueFormats,
392
+ uniqueLibraries,
393
+ uniqueTesting,
394
+ uniqueTools
395
+ }) => {
599
396
  if (!process.env.ESLINT_BASIC_DEBUG) return;
600
397
  process.stdout.write(`[ESLint Basic] Resolved options: ${JSON.stringify({
601
398
  autoFrameworks,
@@ -615,7 +412,16 @@ const logEslintDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStra
615
412
  }, null, 2)}
616
413
  `);
617
414
  };
415
+ const emitBasicWarnings = (options) => {
416
+ if (options?.frameworks && "remix" in options.frameworks) {
417
+ process.emitWarning('[eslint-config-basic] Warning: `frameworks.remix` is deprecated and will be removed in the next major. Please use `frameworks["react-router"]` instead.');
418
+ }
419
+ if (options?.typescript && typeof options.typescript === "object" && "project" in options.typescript) {
420
+ process.emitWarning("[eslint-config-basic] Warning: `typescript.project` is ignored in v2. Type-aware linting now relies on typescript-eslint projectService.");
421
+ }
422
+ };
618
423
  const eslintConfig = async (options, ...extraConfigs) => {
424
+ emitBasicWarnings(options);
619
425
  const {
620
426
  detection,
621
427
  extensions: optExtensions,
@@ -725,7 +531,7 @@ const eslintConfig = async (options, ...extraConfigs) => {
725
531
  uniqueTools,
726
532
  userIgnores
727
533
  });
728
- logEslintDebug(
534
+ logEslintDebug({
729
535
  autoFrameworks,
730
536
  detectRootDir,
731
537
  nextMode,
@@ -740,11 +546,12 @@ const eslintConfig = async (options, ...extraConfigs) => {
740
546
  uniqueLibraries,
741
547
  uniqueTesting,
742
548
  uniqueTools
743
- );
549
+ });
744
550
  const projectConfigs = await Promise.all(
745
551
  Object.entries(configuredProjects).map(async ([projectPath, projectOptions]) => {
746
552
  const projectRoot = join(detectRootDir ?? process.cwd(), projectPath);
747
553
  const scopedConfigs = await eslintConfig({
554
+ autoFrameworks: options?.autoFrameworks,
748
555
  ...projectOptions,
749
556
  detectRootDir: projectOptions.detectRootDir ?? projectRoot,
750
557
  projects: void 0,
@@ -761,7 +568,7 @@ const defineConfig = eslintConfig;
761
568
  export {
762
569
  AGENT_TARGETS,
763
570
  Extension2 as Extension,
764
- Format2 as Format,
571
+ Format,
765
572
  Library2 as Library,
766
573
  NextMode2 as NextMode,
767
574
  Preset2 as Preset,
@@ -769,7 +576,7 @@ export {
769
576
  Runtime2 as Runtime,
770
577
  Setting2 as Setting,
771
578
  Testing2 as Testing,
772
- Tool2 as Tool,
579
+ Tool,
773
580
  a11y,
774
581
  aiSdk,
775
582
  angular,
@@ -778,6 +585,7 @@ export {
778
585
  autogen,
779
586
  bestPractices,
780
587
  biome,
588
+ boundaries,
781
589
  command,
782
590
  compat,
783
591
  coreConfig2 as coreConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@santi020k/eslint-config-basic",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -30,28 +30,28 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@typescript-eslint/utils": "^8.61.1",
33
- "@santi020k/eslint-config-angular": "^2.0.0-beta.1",
34
- "@santi020k/eslint-config-astro": "^2.0.0-beta.1",
35
- "@santi020k/eslint-config-core": "^2.0.0-beta.1",
36
- "@santi020k/eslint-config-expo": "^2.0.0-beta.1",
37
- "@santi020k/eslint-config-hono": "^2.0.0-beta.1",
38
- "@santi020k/eslint-config-integrations": "^2.0.0-beta.1",
39
- "@santi020k/eslint-config-lit": "^2.0.0-beta.1",
40
- "@santi020k/eslint-config-nest": "^2.0.0-beta.1",
41
- "@santi020k/eslint-config-next": "^2.0.0-beta.1",
42
- "@santi020k/eslint-config-nuxt": "^2.0.0-beta.1",
43
- "@santi020k/eslint-config-preact": "^2.0.0-beta.1",
44
- "@santi020k/eslint-config-qwik": "^2.0.0-beta.1",
45
- "@santi020k/eslint-config-react": "^2.0.0-beta.1",
46
- "@santi020k/eslint-config-react-router": "^2.0.0-beta.1",
47
- "@santi020k/eslint-config-remix": "^2.0.0-beta.1",
48
- "@santi020k/eslint-config-slidev": "^2.0.0-beta.1",
49
- "@santi020k/eslint-config-solid": "^2.0.0-beta.1",
50
- "@santi020k/eslint-config-svelte": "^2.0.0-beta.1",
51
- "@santi020k/eslint-config-tanstack-start": "^2.0.0-beta.1",
52
- "@santi020k/eslint-config-typescript": "^2.0.0-beta.1",
53
- "@santi020k/eslint-config-vite": "^2.0.0-beta.1",
54
- "@santi020k/eslint-config-vue": "^2.0.0-beta.1"
33
+ "@santi020k/eslint-config-angular": "^2.0.0",
34
+ "@santi020k/eslint-config-astro": "^2.0.0",
35
+ "@santi020k/eslint-config-core": "^2.0.0",
36
+ "@santi020k/eslint-config-expo": "^2.0.0",
37
+ "@santi020k/eslint-config-hono": "^2.0.0",
38
+ "@santi020k/eslint-config-integrations": "^2.0.0",
39
+ "@santi020k/eslint-config-lit": "^2.0.0",
40
+ "@santi020k/eslint-config-nest": "^2.0.0",
41
+ "@santi020k/eslint-config-next": "^2.0.0",
42
+ "@santi020k/eslint-config-nuxt": "^2.0.0",
43
+ "@santi020k/eslint-config-preact": "^2.0.0",
44
+ "@santi020k/eslint-config-qwik": "^2.0.0",
45
+ "@santi020k/eslint-config-react": "^2.0.0",
46
+ "@santi020k/eslint-config-react-router": "^2.0.0",
47
+ "@santi020k/eslint-config-remix": "^2.0.0",
48
+ "@santi020k/eslint-config-slidev": "^2.0.0",
49
+ "@santi020k/eslint-config-solid": "^2.0.0",
50
+ "@santi020k/eslint-config-svelte": "^2.0.0",
51
+ "@santi020k/eslint-config-tanstack-start": "^2.0.0",
52
+ "@santi020k/eslint-config-typescript": "^2.0.0",
53
+ "@santi020k/eslint-config-vite": "^2.0.0",
54
+ "@santi020k/eslint-config-vue": "^2.0.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^25.9.3",
package/dist/compose.d.ts DELETED
@@ -1,10 +0,0 @@
1
- import { FlatConfigArray } from '@santi020k/eslint-config-core';
2
-
3
- type NormalizedStrictMode = 'ci' | 'pedantic' | 'recommended';
4
- declare const normalizeStrictMode: (strict: boolean | NormalizedStrictMode | undefined) => NormalizedStrictMode;
5
- /**
6
- * Applies strict mode by promoting all 'warn' rules to 'error'.
7
- */
8
- declare const applyStrictMode: (configs: FlatConfigArray, strict: boolean | NormalizedStrictMode | undefined) => FlatConfigArray;
9
-
10
- export { type NormalizedStrictMode, applyStrictMode, normalizeStrictMode };
package/dist/compose.js DELETED
@@ -1,34 +0,0 @@
1
- const normalizeStrictMode = (strict) => {
2
- if (strict === true) return "ci";
3
- if (strict === "ci" || strict === "pedantic") return strict;
4
- return "recommended";
5
- };
6
- const promoteRuleSeverity = (value) => {
7
- if (value === void 0) return void 0;
8
- if (value === "warn" || value === 1) return "error";
9
- if (Array.isArray(value) && (value[0] === "warn" || value[0] === 1)) {
10
- return ["error", ...value.slice(1)];
11
- }
12
- return value;
13
- };
14
- const applyStrictMode = (configs, strict) => {
15
- const strictMode = normalizeStrictMode(strict);
16
- if (strictMode === "recommended") return configs;
17
- const recurse = (item) => {
18
- if (Array.isArray(item)) {
19
- return item.map((config) => recurse(config));
20
- }
21
- if (item.rules) {
22
- const strictRules = Object.fromEntries(
23
- Object.entries(item.rules).map(([key, value]) => [key, promoteRuleSeverity(value)])
24
- );
25
- return { ...item, rules: strictRules };
26
- }
27
- return item;
28
- };
29
- return recurse(configs);
30
- };
31
- export {
32
- applyStrictMode,
33
- normalizeStrictMode
34
- };
package/dist/lazy.d.ts DELETED
@@ -1,3 +0,0 @@
1
- declare const loadModule: <T = unknown>(specifier: string) => Promise<T>;
2
-
3
- export { loadModule };
package/dist/lazy.js DELETED
@@ -1,19 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import { pathToFileURL } from "node:url";
3
- const isVitest = typeof process !== "undefined" && process.env.VITEST;
4
- const dynamicImport = isVitest ? (specifier) => import(
5
- /* @vite-ignore */
6
- specifier
7
- ) : Reflect.construct(Function, ["specifier", "return import(specifier)"]);
8
- const req = createRequire(import.meta.url);
9
- const loadModule = async (specifier) => {
10
- let resolved = specifier;
11
- try {
12
- resolved = pathToFileURL(req.resolve(specifier)).href;
13
- } catch {
14
- }
15
- return await dynamicImport(resolved);
16
- };
17
- export {
18
- loadModule
19
- };