@santi020k/eslint-config-basic 2.0.0-beta.1 → 2.0.0-beta.3

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,69 @@
1
1
  # @santi020k/eslint-config-basic
2
2
 
3
+ ## 2.0.0-beta.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`af152cd`](https://github.com/santi020k/eslint-config-basic/commit/af152cda17961f9de8ca7bf069739f151fca65be)]:
8
+ - @santi020k/eslint-config-core@2.0.0-beta.3
9
+ - @santi020k/eslint-config-integrations@2.0.0-beta.3
10
+ - @santi020k/eslint-config-typescript@2.0.0-beta.3
11
+ - @santi020k/eslint-config-angular@2.0.0-beta.3
12
+ - @santi020k/eslint-config-astro@2.0.0-beta.3
13
+ - @santi020k/eslint-config-expo@2.0.0-beta.3
14
+ - @santi020k/eslint-config-hono@2.0.0-beta.3
15
+ - @santi020k/eslint-config-lit@2.0.0-beta.3
16
+ - @santi020k/eslint-config-nest@2.0.0-beta.3
17
+ - @santi020k/eslint-config-next@2.0.0-beta.3
18
+ - @santi020k/eslint-config-nuxt@2.0.0-beta.3
19
+ - @santi020k/eslint-config-qwik@2.0.0-beta.3
20
+ - @santi020k/eslint-config-react@2.0.0-beta.3
21
+ - @santi020k/eslint-config-react-router@2.0.0-beta.3
22
+ - @santi020k/eslint-config-remix@2.0.0-beta.3
23
+ - @santi020k/eslint-config-slidev@2.0.0-beta.3
24
+ - @santi020k/eslint-config-solid@2.0.0-beta.3
25
+ - @santi020k/eslint-config-svelte@2.0.0-beta.3
26
+ - @santi020k/eslint-config-tanstack-start@2.0.0-beta.3
27
+ - @santi020k/eslint-config-vite@2.0.0-beta.3
28
+ - @santi020k/eslint-config-vue@2.0.0-beta.3
29
+ - @santi020k/eslint-config-preact@2.0.0-beta.3
30
+
31
+ ## 2.0.0-beta.2
32
+
33
+ ### Minor Changes
34
+
35
+ - Tailwind `noUnknownClasses` option, expanded testing config names, and virtual TS parser fix.
36
+
37
+ - **`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.
38
+ - **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.
39
+ - **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.
40
+
41
+ ### Patch Changes
42
+
43
+ - Updated dependencies []:
44
+ - @santi020k/eslint-config-core@2.0.0-beta.2
45
+ - @santi020k/eslint-config-typescript@2.0.0-beta.2
46
+ - @santi020k/eslint-config-integrations@2.0.0-beta.2
47
+ - @santi020k/eslint-config-angular@2.0.0-beta.2
48
+ - @santi020k/eslint-config-astro@2.0.0-beta.2
49
+ - @santi020k/eslint-config-expo@2.0.0-beta.2
50
+ - @santi020k/eslint-config-hono@2.0.0-beta.2
51
+ - @santi020k/eslint-config-lit@2.0.0-beta.2
52
+ - @santi020k/eslint-config-nest@2.0.0-beta.2
53
+ - @santi020k/eslint-config-next@2.0.0-beta.2
54
+ - @santi020k/eslint-config-nuxt@2.0.0-beta.2
55
+ - @santi020k/eslint-config-qwik@2.0.0-beta.2
56
+ - @santi020k/eslint-config-react@2.0.0-beta.2
57
+ - @santi020k/eslint-config-react-router@2.0.0-beta.2
58
+ - @santi020k/eslint-config-remix@2.0.0-beta.2
59
+ - @santi020k/eslint-config-slidev@2.0.0-beta.2
60
+ - @santi020k/eslint-config-solid@2.0.0-beta.2
61
+ - @santi020k/eslint-config-svelte@2.0.0-beta.2
62
+ - @santi020k/eslint-config-tanstack-start@2.0.0-beta.2
63
+ - @santi020k/eslint-config-vite@2.0.0-beta.2
64
+ - @santi020k/eslint-config-vue@2.0.0-beta.2
65
+ - @santi020k/eslint-config-preact@2.0.0-beta.2
66
+
3
67
  ## 2.0.0-beta.1
4
68
 
5
69
  ### 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
5
  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';
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,32 @@
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
- Extension,
8
- Format,
10
+ findTailwindEntryPoint,
11
+ flattenConfigInputs,
12
+ GENERATED_CODE_IGNORES,
13
+ getStrictMode,
9
14
  gitignore,
10
15
  Library,
16
+ mergeFrameworkOption,
17
+ mergeOptionalBucket,
11
18
  NextMode,
19
+ patchImportGroups,
12
20
  Preset,
21
+ resolveTsconfigRootDir,
22
+ resolveTypescriptOptions,
13
23
  Runtime,
24
+ scopeConfigToProject,
14
25
  Setting,
15
- Testing,
16
- Tool
26
+ Testing
17
27
  } from "@santi020k/eslint-config-core";
28
+ import { tailwind as tailwindIntegration } from "@santi020k/eslint-config-integrations";
18
29
  import { createTypescriptConfig } from "@santi020k/eslint-config-typescript";
19
- import { applyStrictMode } from "./compose.js";
20
30
  import { createDetectedFrameworkFlags } from "./frameworks.js";
21
31
  import { getIntegrationConfigs, getPrettierConfig } from "./integrations.js";
22
32
  import { resolveFramework, resolvePreset } from "./resolvers.js";
@@ -60,8 +70,8 @@ import {
60
70
  createCoreConfig as createCoreConfig2,
61
71
  createImportGroups,
62
72
  detectProjectOptions as detectProjectOptions2,
63
- Extension as Extension2,
64
- Format as Format2,
73
+ Extension,
74
+ Format,
65
75
  getGlobalsForRuntime,
66
76
  gitignore as gitignore2,
67
77
  groups,
@@ -74,7 +84,7 @@ import {
74
84
  Runtime as Runtime2,
75
85
  Setting as Setting2,
76
86
  Testing as Testing2,
77
- Tool as Tool2
87
+ Tool
78
88
  } from "@santi020k/eslint-config-core";
79
89
  import {
80
90
  a11y,
@@ -138,270 +148,54 @@ import {
138
148
  zod
139
149
  } from "@santi020k/eslint-config-integrations";
140
150
  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
151
  const buildTailwindResult = (options, entryPoint) => ({
327
152
  detectComponentClasses: options.detectComponentClasses ?? true,
328
153
  ...entryPoint ? { entryPoint } : {},
329
- ...options.ignore?.length ? { ignore: options.ignore } : {}
154
+ ...options.ignore?.length ? { ignore: options.ignore } : {},
155
+ ...options.noUnknownClasses === void 0 ? {} : { noUnknownClasses: options.noUnknownClasses }
330
156
  });
331
157
  const resolveTailwindOptions = (rootDir, tailwind2) => {
332
158
  if (tailwind2 === false) return void 0;
333
159
  const options = tailwind2 ?? {};
334
160
  const entryPoint = options.entryPoint ?? findTailwindEntryPoint(rootDir);
335
- if (!entryPoint && options.detectComponentClasses === void 0 && !options.ignore?.length) return void 0;
161
+ if (!entryPoint && options.detectComponentClasses === void 0 && !options.ignore?.length && options.noUnknownClasses === void 0) return void 0;
336
162
  return buildTailwindResult(options, entryPoint);
337
163
  };
338
- const createTailwindSettingsConfig = (tailwindOptions) => {
164
+ const createNoUnknownClassesRule = (tailwindOptions, unknownClassOptions, hasUnknownClassOptions) => {
165
+ if (!hasUnknownClassOptions && tailwindOptions.noUnknownClasses === void 0) return void 0;
166
+ const severity = tailwindOptions.noUnknownClasses ?? "error";
167
+ if (severity === false) return "off";
168
+ return hasUnknownClassOptions ? [severity, unknownClassOptions] : severity;
169
+ };
170
+ const loadTailwindPlugins = async () => {
171
+ const tailwindConfigs = await tailwindIntegration();
172
+ const pluginConfig = tailwindConfigs.find((config) => config.plugins?.["better-tailwindcss"]);
173
+ return pluginConfig?.plugins ?? {};
174
+ };
175
+ const createTailwindSettingsConfig = async (tailwindOptions) => {
176
+ const { noUnknownClasses: _noUnknownClasses, ...settingsOptions } = tailwindOptions;
339
177
  const unknownClassOptions = {
340
178
  ...tailwindOptions.entryPoint ? { entryPoint: tailwindOptions.entryPoint } : {},
341
179
  ...tailwindOptions.ignore?.length ? { ignore: tailwindOptions.ignore } : {}
342
180
  };
343
181
  const hasUnknownClassOptions = Object.keys(unknownClassOptions).length > 0;
182
+ const noUnknownClassesRule = createNoUnknownClassesRule(tailwindOptions, unknownClassOptions, hasUnknownClassOptions);
344
183
  return {
345
184
  name: "eslint-config-basic/tailwind-settings",
346
- ...hasUnknownClassOptions ? { rules: { "better-tailwindcss/no-unknown-classes": ["error", unknownClassOptions] } } : {},
185
+ plugins: await loadTailwindPlugins(),
186
+ ...noUnknownClassesRule === void 0 ? {} : { rules: { "better-tailwindcss/no-unknown-classes": noUnknownClassesRule } },
347
187
  settings: {
348
- "better-tailwindcss": tailwindOptions
188
+ "better-tailwindcss": settingsOptions
349
189
  }
350
190
  };
351
191
  };
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
- ]
392
- }
393
- };
394
- };
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
192
  const TESTING_CONFIG_NAMES = {
404
- [Testing.Playwright]: ["integrations/playwright"]
193
+ [Testing.Cypress]: ["integrations/cypress"],
194
+ [Testing.Jest]: ["integrations/jest"],
195
+ [Testing.JestDom]: ["eslint-config-integrations/jest-dom"],
196
+ [Testing.Playwright]: ["integrations/playwright"],
197
+ [Testing.TestingLibrary]: ["integrations/testing-library"],
198
+ [Testing.Vitest]: ["integrations/vitest"]
405
199
  };
406
200
  const applyTestingFileOverrides = (configs, testingFiles) => {
407
201
  if (!testingFiles) return configs;
@@ -473,6 +267,23 @@ const buildIgnoresConfig = (useDefaultIgnores, useGeneratedCodeIgnores, useGitig
473
267
  name: "eslint-config-basic/ignores"
474
268
  }] : []
475
269
  });
270
+ const FRAMEWORK_EXTRA_OPTS = {
271
+ astro: (ctx) => ({ hasReact: ctx.hasReact, hasSolid: ctx.hasSolid, hasSvelte: ctx.hasSvelte, hasVue: ctx.hasVue }),
272
+ hono: (ctx) => ({ runtime: ctx.runtime }),
273
+ slidev: (ctx) => ({ runtime: ctx.runtime }),
274
+ vite: (ctx) => ({ runtime: ctx.runtime })
275
+ };
276
+ const resolveEnabledFrameworks = async (frameworks, ctx) => {
277
+ const entries = await Promise.all(
278
+ Object.entries(frameworks).filter((entry) => Boolean(entry[1])).map(
279
+ ([name, value]) => (
280
+ // eslint-disable-next-line security/detect-object-injection
281
+ resolveFramework(name, value, FRAMEWORK_EXTRA_OPTS[name]?.(ctx)).then((config) => [name, config])
282
+ )
283
+ )
284
+ );
285
+ return Object.fromEntries(entries);
286
+ };
476
287
  const buildEslintConfigs = async (params) => {
477
288
  const {
478
289
  astroOptions,
@@ -495,47 +306,8 @@ const buildEslintConfigs = async (params) => {
495
306
  userIgnores
496
307
  } = params;
497
308
  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
- ]);
309
+ const fw = await resolveEnabledFrameworks(resolvedFrameworks, { hasReact, hasSolid, hasSvelte, hasVue, runtime });
310
+ const get = (name) => fw[name] ?? [];
539
311
  return [
540
312
  ...defaultIgnores,
541
313
  ...userIgnores,
@@ -559,29 +331,29 @@ const buildEslintConfigs = async (params) => {
559
331
  name: "eslint-config-basic/commonjs"
560
332
  },
561
333
  ...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,
334
+ ...hasReact ? get("react") : [],
335
+ ...get("next"),
336
+ ...get("expo"),
337
+ ...get("nest"),
338
+ ...get("hono"),
339
+ ...get("vue"),
340
+ ...get("svelte"),
341
+ ...get("solid"),
342
+ ...get("angular"),
343
+ ...get("qwik"),
344
+ ...get("remix"),
345
+ ...get("react-router"),
346
+ ...get("tanstack-start"),
347
+ ...get("nuxt"),
348
+ ...get("preact"),
349
+ ...get("lit"),
350
+ ...get("slidev"),
351
+ ...get("vite"),
580
352
  ...resolvedTypescript ? createTypescriptConfig({
581
353
  ...resolvedTypescript,
582
354
  tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
583
355
  }) : [],
584
- ...astroParam,
356
+ ...get("astro"),
585
357
  ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [{
586
358
  files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
587
359
  name: "eslint-config-next/app-router-overrides",
@@ -591,11 +363,26 @@ const buildEslintConfigs = async (params) => {
591
363
  await getIntegrationConfigs(uniqueLibraries, uniqueTools, uniqueTesting, uniqueFormats, uniqueExtensions),
592
364
  testingFiles
593
365
  ),
594
- ...tailwindOptions ? [createTailwindSettingsConfig(tailwindOptions)] : [],
366
+ ...tailwindOptions ? [await createTailwindSettingsConfig(tailwindOptions)] : [],
595
367
  ...await getPrettierConfig(uniqueTools)
596
368
  ];
597
369
  };
598
- const logEslintDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStrategy, preset, resolvedFrameworks, resolvedTypescript, runtime, tsconfigRootDir, uniqueExtensions, uniqueFormats, uniqueLibraries, uniqueTesting, uniqueTools) => {
370
+ const logEslintDebug = ({
371
+ autoFrameworks,
372
+ detectRootDir,
373
+ nextMode,
374
+ optionMergeStrategy,
375
+ preset,
376
+ resolvedFrameworks,
377
+ resolvedTypescript,
378
+ runtime,
379
+ tsconfigRootDir,
380
+ uniqueExtensions,
381
+ uniqueFormats,
382
+ uniqueLibraries,
383
+ uniqueTesting,
384
+ uniqueTools
385
+ }) => {
599
386
  if (!process.env.ESLINT_BASIC_DEBUG) return;
600
387
  process.stdout.write(`[ESLint Basic] Resolved options: ${JSON.stringify({
601
388
  autoFrameworks,
@@ -615,7 +402,16 @@ const logEslintDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStra
615
402
  }, null, 2)}
616
403
  `);
617
404
  };
405
+ const emitBasicWarnings = (options) => {
406
+ if (options?.frameworks && "remix" in options.frameworks) {
407
+ process.emitWarning('[eslint-config-basic] Warning: `frameworks.remix` is deprecated and will be removed in the next major. Please use `frameworks["react-router"]` instead.');
408
+ }
409
+ if (options?.typescript && typeof options.typescript === "object" && "project" in options.typescript) {
410
+ process.emitWarning("[eslint-config-basic] Warning: `typescript.project` is ignored in v2. Type-aware linting now relies on typescript-eslint projectService.");
411
+ }
412
+ };
618
413
  const eslintConfig = async (options, ...extraConfigs) => {
414
+ emitBasicWarnings(options);
619
415
  const {
620
416
  detection,
621
417
  extensions: optExtensions,
@@ -725,7 +521,7 @@ const eslintConfig = async (options, ...extraConfigs) => {
725
521
  uniqueTools,
726
522
  userIgnores
727
523
  });
728
- logEslintDebug(
524
+ logEslintDebug({
729
525
  autoFrameworks,
730
526
  detectRootDir,
731
527
  nextMode,
@@ -740,11 +536,12 @@ const eslintConfig = async (options, ...extraConfigs) => {
740
536
  uniqueLibraries,
741
537
  uniqueTesting,
742
538
  uniqueTools
743
- );
539
+ });
744
540
  const projectConfigs = await Promise.all(
745
541
  Object.entries(configuredProjects).map(async ([projectPath, projectOptions]) => {
746
542
  const projectRoot = join(detectRootDir ?? process.cwd(), projectPath);
747
543
  const scopedConfigs = await eslintConfig({
544
+ autoFrameworks: options?.autoFrameworks,
748
545
  ...projectOptions,
749
546
  detectRootDir: projectOptions.detectRootDir ?? projectRoot,
750
547
  projects: void 0,
@@ -760,8 +557,8 @@ const eslintConfig = async (options, ...extraConfigs) => {
760
557
  const defineConfig = eslintConfig;
761
558
  export {
762
559
  AGENT_TARGETS,
763
- Extension2 as Extension,
764
- Format2 as Format,
560
+ Extension,
561
+ Format,
765
562
  Library2 as Library,
766
563
  NextMode2 as NextMode,
767
564
  Preset2 as Preset,
@@ -769,7 +566,7 @@ export {
769
566
  Runtime2 as Runtime,
770
567
  Setting2 as Setting,
771
568
  Testing2 as Testing,
772
- Tool2 as Tool,
569
+ Tool,
773
570
  a11y,
774
571
  aiSdk,
775
572
  angular,
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-beta.3",
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-beta.3",
34
+ "@santi020k/eslint-config-astro": "^2.0.0-beta.3",
35
+ "@santi020k/eslint-config-core": "^2.0.0-beta.3",
36
+ "@santi020k/eslint-config-expo": "^2.0.0-beta.3",
37
+ "@santi020k/eslint-config-hono": "^2.0.0-beta.3",
38
+ "@santi020k/eslint-config-integrations": "^2.0.0-beta.3",
39
+ "@santi020k/eslint-config-lit": "^2.0.0-beta.3",
40
+ "@santi020k/eslint-config-nest": "^2.0.0-beta.3",
41
+ "@santi020k/eslint-config-next": "^2.0.0-beta.3",
42
+ "@santi020k/eslint-config-nuxt": "^2.0.0-beta.3",
43
+ "@santi020k/eslint-config-preact": "^2.0.0-beta.3",
44
+ "@santi020k/eslint-config-qwik": "^2.0.0-beta.3",
45
+ "@santi020k/eslint-config-react": "^2.0.0-beta.3",
46
+ "@santi020k/eslint-config-react-router": "^2.0.0-beta.3",
47
+ "@santi020k/eslint-config-remix": "^2.0.0-beta.3",
48
+ "@santi020k/eslint-config-slidev": "^2.0.0-beta.3",
49
+ "@santi020k/eslint-config-solid": "^2.0.0-beta.3",
50
+ "@santi020k/eslint-config-svelte": "^2.0.0-beta.3",
51
+ "@santi020k/eslint-config-tanstack-start": "^2.0.0-beta.3",
52
+ "@santi020k/eslint-config-typescript": "^2.0.0-beta.3",
53
+ "@santi020k/eslint-config-vite": "^2.0.0-beta.3",
54
+ "@santi020k/eslint-config-vue": "^2.0.0-beta.3"
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
- };