@santi020k/eslint-config-basic 1.6.0 → 2.0.0-beta.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/dist/index.js CHANGED
@@ -1,57 +1,126 @@
1
- import { applyStrictMode } from "./compose.js";
2
- import { getOptionalConfigs, getPrettierConfig } from "./optionals.js";
3
- import { resolveFramework, resolvePreset } from "./resolvers.js";
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
4
3
  import {
5
4
  coreConfig,
6
5
  createCoreConfig,
7
6
  detectProjectOptions,
7
+ Extension,
8
+ Format,
8
9
  gitignore,
10
+ Library,
9
11
  NextMode,
12
+ Preset,
10
13
  Runtime,
11
- Setting
14
+ Setting,
15
+ Testing,
16
+ Tool
12
17
  } from "@santi020k/eslint-config-core";
13
18
  import { createTypescriptConfig } from "@santi020k/eslint-config-typescript";
19
+ import { applyStrictMode } from "./compose.js";
20
+ import { createDetectedFrameworkFlags } from "./frameworks.js";
21
+ import { getIntegrationConfigs, getPrettierConfig } from "./integrations.js";
22
+ import { resolveFramework, resolvePreset } from "./resolvers.js";
14
23
  import {
15
24
  AGENT_TARGETS,
16
25
  generateAgentSkills,
17
26
  generateSkillContent
18
27
  } from "./agent-skill-generator.js";
28
+ import {
29
+ angular,
30
+ angularConfig,
31
+ astro,
32
+ expo,
33
+ expoConfig,
34
+ hono,
35
+ lit,
36
+ nest,
37
+ nestConfig,
38
+ next,
39
+ nextConfig,
40
+ nuxt,
41
+ preact,
42
+ preactConfig,
43
+ qwik,
44
+ react,
45
+ reactConfig,
46
+ reactRouter,
47
+ remix,
48
+ slidev,
49
+ solid,
50
+ solidConfig,
51
+ svelte,
52
+ svelteConfig,
53
+ tanstackStart,
54
+ vite,
55
+ vue,
56
+ vueConfig
57
+ } from "./frameworks.js";
19
58
  import {
20
59
  coreConfig as coreConfig2,
21
60
  createCoreConfig as createCoreConfig2,
61
+ createImportGroups,
22
62
  detectProjectOptions as detectProjectOptions2,
23
- Extension,
24
- Format,
63
+ Extension as Extension2,
64
+ Format as Format2,
25
65
  getGlobalsForRuntime,
26
66
  gitignore as gitignore2,
67
+ groups,
27
68
  hasReactConfig,
28
69
  jsConfig,
29
- Library,
70
+ Library as Library2,
30
71
  NextMode as NextMode2,
31
- Preset,
72
+ Preset as Preset2,
32
73
  ReactConfigKeys,
33
74
  Runtime as Runtime2,
34
75
  Setting as Setting2,
35
- Testing,
36
- Tool
76
+ Testing as Testing2,
77
+ Tool as Tool2
37
78
  } from "@santi020k/eslint-config-core";
38
- import { tsConfig, typescriptConfig } from "@santi020k/eslint-config-typescript";
39
79
  import {
80
+ a11y,
81
+ aiSdk,
82
+ autogen,
40
83
  bestPractices,
84
+ biome,
85
+ command,
86
+ compat,
41
87
  cspell,
88
+ css,
42
89
  cypress,
90
+ deMorgan,
91
+ depend,
92
+ docker,
93
+ drizzle,
94
+ githubActions,
95
+ googleGenAi,
43
96
  graphql,
97
+ html,
44
98
  i18next,
45
99
  jest,
100
+ jestDom,
46
101
  jsdoc,
47
102
  jsonc,
103
+ langchain,
104
+ llamaIndex,
48
105
  markdown,
106
+ mastra,
107
+ mcp,
49
108
  mdx,
109
+ mikroOrm,
110
+ node,
111
+ noOnlyTests,
112
+ nx,
113
+ openAiAgents,
114
+ oxlint,
115
+ packageJson,
50
116
  perfectionist,
51
117
  playwright,
118
+ pnpm,
52
119
  prettier,
120
+ prisma,
53
121
  regexp,
54
122
  security,
123
+ sequelize,
55
124
  sonarjs,
56
125
  stencil,
57
126
  storybook,
@@ -61,77 +130,379 @@ import {
61
130
  tanstackRouter,
62
131
  testingLibrary,
63
132
  toml,
133
+ turbo,
134
+ typeorm,
64
135
  unicorn,
65
136
  vitest,
66
- yaml
67
- } from "@santi020k/eslint-config-optionals";
68
- const eslintConfig = (options) => {
69
- const detected = detectProjectOptions();
70
- const preset = options?.preset ?? detected.preset;
71
- const presetDefaults = preset ? resolvePreset(preset) : {};
72
- const {
73
- typescript = presetDefaults.typescript ?? detected.typescript ?? false,
74
- libraries = presetDefaults.libraries ?? detected.libraries ?? [],
75
- testing = presetDefaults.testing ?? detected.testing ?? [],
76
- formats = presetDefaults.formats ?? detected.formats ?? [],
77
- tools = presetDefaults.tools ?? detected.tools ?? [],
78
- extensions = presetDefaults.extensions ?? detected.extensions ?? [],
79
- settings = options?.settings ?? detected.settings ?? [],
80
- strict = options?.strict ?? false,
81
- runtime = presetDefaults.runtime ?? detected.runtime ?? Runtime.Universal,
82
- tsconfigRootDir = options?.tsconfigRootDir,
83
- nextMode = presetDefaults.nextMode ?? detected.nextMode ?? NextMode.Pages,
84
- frameworks = { ...presetDefaults.frameworks, ...options?.frameworks }
85
- } = options ?? {};
86
- const uniqueLibraries = [...new Set(libraries)];
87
- const uniqueTesting = [...new Set(testing)];
88
- const uniqueFormats = [...new Set(formats)];
89
- const uniqueTools = [...new Set(tools)];
90
- const uniqueExtensions = [...new Set(extensions)];
91
- const uniqueSettings = [...new Set(settings)];
92
- if ((frameworks.next || frameworks.expo) && !frameworks.react) {
93
- throw new TypeError(
94
- "Next and Expo configurations require frameworks.react. Import @santi020k/eslint-config-react and pass it via frameworks.react."
137
+ yaml,
138
+ zod
139
+ } from "@santi020k/eslint-config-integrations";
140
+ 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 mergeArrayOption = (detectedValues, presetValues, explicitValues, strategy) => {
206
+ if (strategy === "replace") {
207
+ if (explicitValues) return toUniqueArray(explicitValues);
208
+ if (presetValues) return toUniqueArray(presetValues);
209
+ return toUniqueArray(detectedValues);
210
+ }
211
+ return toUniqueArray([
212
+ ...detectedValues,
213
+ ...presetValues ?? [],
214
+ ...explicitValues ?? []
215
+ ]);
216
+ };
217
+ const isOptionalBucketValue = (bucket, value) => OPTIONAL_BUCKETS[bucket].includes(value);
218
+ const getFeatureEntries = (options, bucket, enabled) => [
219
+ ...Object.entries(options?.features ?? {}),
220
+ ...Object.entries(options?.integrations ?? {})
221
+ ].filter(([name, value]) => value === enabled && isOptionalBucketValue(bucket, name)).map(([name]) => name);
222
+ const applyFeatureDisables = (values, options, bucket) => {
223
+ const disabled = new Set(getFeatureEntries(options, bucket, false));
224
+ return values.filter((value) => !disabled.has(value));
225
+ };
226
+ const mergeOptionalBucket = (bucket, detectedValues, presetValues, explicitValues, options, strategy) => {
227
+ const featureEntries = getFeatureEntries(options, bucket, true);
228
+ const combined = explicitValues !== void 0 || featureEntries.length > 0 ? [...explicitValues ?? [], ...featureEntries] : void 0;
229
+ return applyFeatureDisables(
230
+ mergeArrayOption(detectedValues, presetValues, combined, strategy),
231
+ options,
232
+ bucket
233
+ );
234
+ };
235
+ const mergeFrameworkOption = (detectedFrameworks, presetFrameworks, explicitFrameworks, strategy) => {
236
+ if (strategy === "replace") {
237
+ if (explicitFrameworks) return { ...explicitFrameworks };
238
+ if (presetFrameworks) return { ...presetFrameworks };
239
+ return { ...detectedFrameworks };
240
+ }
241
+ return {
242
+ ...detectedFrameworks,
243
+ ...presetFrameworks ?? {},
244
+ ...explicitFrameworks ?? {}
245
+ };
246
+ };
247
+ const resolveDetectionOptions = (detection, defaults = {}) => {
248
+ const defaultControls = {
249
+ extensions: true,
250
+ formats: true,
251
+ frameworks: true,
252
+ libraries: true,
253
+ nextMode: true,
254
+ projects: false,
255
+ runtime: true,
256
+ testing: true,
257
+ tools: true,
258
+ typescript: true,
259
+ ...defaults
260
+ };
261
+ if (detection === false) {
262
+ return Object.fromEntries(
263
+ Object.keys(defaultControls).map((key) => [key, false])
95
264
  );
96
265
  }
97
- const hasReact = !!frameworks.react;
98
- const hasVue = !!frameworks.vue;
99
- const hasSvelte = !!frameworks.svelte;
100
- const hasSolid = !!frameworks.solid;
101
- const useGitignore = !uniqueSettings.includes(Setting.NoGitignore);
102
- const reactParam = resolveFramework("react", frameworks.react);
103
- const nextParam = resolveFramework("next", frameworks.next);
104
- const astroParam = resolveFramework("astro", frameworks.astro, {
105
- hasReact,
106
- hasVue,
107
- hasSvelte,
108
- hasSolid
109
- });
110
- const expoParam = resolveFramework("expo", frameworks.expo);
111
- const nestParam = resolveFramework("nest", frameworks.nest);
112
- const honoParam = resolveFramework("hono", frameworks.hono, { runtime });
113
- const vueParam = resolveFramework("vue", frameworks.vue);
114
- const svelteParam = resolveFramework("svelte", frameworks.svelte);
115
- const solidParam = resolveFramework("solid", frameworks.solid);
116
- const angularParam = resolveFramework("angular", frameworks.angular);
117
- const qwikParam = resolveFramework("qwik", frameworks.qwik);
118
- const remixParam = resolveFramework("remix", frameworks.remix);
119
- const runtimeCoreConfig = runtime !== Runtime.Universal ? createCoreConfig(runtime) : coreConfig;
120
- const configs = [
121
- // Settings
122
- ...useGitignore ? gitignore : [],
123
- // Global TSConfig Root Dir fix
266
+ if (detection === true || detection === void 0) {
267
+ return defaultControls;
268
+ }
269
+ return {
270
+ ...defaultControls,
271
+ ...detection
272
+ };
273
+ };
274
+ const applyArrayControls = (controls, detected) => ({
275
+ detectedFrameworks: controls.frameworks ? detected.detectedFrameworks : [],
276
+ extensions: controls.extensions ? detected.extensions : [],
277
+ formats: controls.formats ? detected.formats : [],
278
+ libraries: controls.libraries ? detected.libraries : [],
279
+ testing: controls.testing ? detected.testing : [],
280
+ tools: controls.tools ? detected.tools : []
281
+ });
282
+ const applyScalarControls = (controls, detected) => ({
283
+ nextMode: controls.nextMode ? detected.nextMode : void 0,
284
+ preset: controls.typescript && controls.runtime ? detected.preset : void 0,
285
+ projects: controls.projects ? detected.projects : void 0,
286
+ runtime: controls.runtime ? detected.runtime : void 0,
287
+ typescript: controls.typescript ? detected.typescript : false
288
+ });
289
+ const applyDetectionControls = (detected, detection, defaults) => {
290
+ const controls = resolveDetectionOptions(detection, defaults);
291
+ return { ...detected, ...applyArrayControls(controls, detected), ...applyScalarControls(controls, detected) };
292
+ };
293
+ const getStrictMode = (explicitStrict, presetStrict) => explicitStrict ?? presetStrict ?? false;
294
+ const applyStrictProfileDefaults = (extensions, strict) => {
295
+ if (strict !== "pedantic") return extensions;
296
+ return toUniqueArray([...extensions, Extension.BestPractices]);
297
+ };
298
+ const hasTsconfig = (rootDir) => [
299
+ "tsconfig.eslint.json",
300
+ "tsconfig.json",
301
+ "tsconfig.base.json",
302
+ "tsconfig.app.json",
303
+ "tsconfig.node.json"
304
+ ].some((fileName) => existsSync(join(rootDir, fileName)));
305
+ const resolveTypescriptOptions = (typescript) => {
306
+ if (!typescript || typescript === "off") return false;
307
+ if (typescript === true) return { mode: "type-aware" };
308
+ if (typeof typescript === "string") return { mode: typescript };
309
+ if (typescript.mode === "off") return false;
310
+ return {
311
+ ...typescript,
312
+ mode: typescript.mode ?? "type-aware"
313
+ };
314
+ };
315
+ const resolveTsconfigRootDir = (rootDir, typescript, explicitRootDir) => {
316
+ if (explicitRootDir) return explicitRootDir;
317
+ return resolveTypescriptOptions(typescript) && hasTsconfig(rootDir) ? rootDir : void 0;
318
+ };
319
+ const findTailwindEntryPoint = (rootDir) => TAILWIND_ENTRYPOINT_CANDIDATES.find(
320
+ (candidate) => existsSync(join(rootDir, candidate))
321
+ );
322
+ const scopeFilePattern = (projectPath, pattern) => {
323
+ if (typeof pattern === "string") {
324
+ const isNegated = pattern.startsWith("!");
325
+ const basePattern = (isNegated ? pattern.slice(1) : pattern).replace(/^\.\//, "");
326
+ const scoped = `${projectPath.replace(/\/$/, "")}/${basePattern}`;
327
+ return isNegated ? `!${scoped}` : scoped;
328
+ }
329
+ if (Array.isArray(pattern)) {
330
+ return pattern.map((item) => scopeFilePattern(projectPath, item));
331
+ }
332
+ return pattern;
333
+ };
334
+ const scopeConfigToProject = (config, projectPath) => {
335
+ if ("ignores" in config && !config.files) {
336
+ return {
337
+ ...config,
338
+ ignores: config.ignores?.map((pattern) => scopeFilePattern(projectPath, pattern))
339
+ };
340
+ }
341
+ return {
342
+ ...config,
343
+ files: Array.isArray(config.files) ? config.files.map((pattern) => scopeFilePattern(projectPath, pattern)) : [`${projectPath.replace(/\/$/, "")}/**/*`]
344
+ };
345
+ };
346
+ const patchImportGroupsConfig = (config, workspacePatterns) => {
347
+ const rule = config.rules?.["simple-import-sort/imports"];
348
+ if (!Array.isArray(rule) || !rule[1]) return config;
349
+ const ruleOpts = rule[1];
350
+ const existingGroups = ruleOpts.groups ?? [];
351
+ if (existingGroups.length === 0) return config;
352
+ const externalIdx = existingGroups.findIndex((g) => g.some((p) => p.includes("^@?\\w")));
353
+ const insertAt = externalIdx >= 0 ? externalIdx : existingGroups.length;
354
+ return {
355
+ ...config,
356
+ rules: {
357
+ ...config.rules,
358
+ "simple-import-sort/imports": [
359
+ rule[0],
360
+ { ...ruleOpts, groups: [...existingGroups.slice(0, insertAt), workspacePatterns, ...existingGroups.slice(insertAt)] }
361
+ ]
362
+ }
363
+ };
364
+ };
365
+ const patchImportGroups = (allConfigs, workspacePrefixes) => {
366
+ const workspacePatterns = workspacePrefixes.map(
367
+ (p) => `^${p.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")}(/.*|$)`
368
+ );
369
+ return allConfigs.map(
370
+ (item) => Array.isArray(item) ? item : patchImportGroupsConfig(item, workspacePatterns)
371
+ );
372
+ };
373
+ const resolveConfigSetup = (options) => ({
374
+ autoFrameworks: options?.autoFrameworks ?? true,
375
+ detectRootDir: options?.detectRootDir ?? options?.tsconfigRootDir,
376
+ optionMergeStrategy: options?.optionMergeStrategy ?? "merge",
377
+ requestedPreset: options?.preset
378
+ });
379
+ const resolvePresetMeta = (requestedPreset, detected, autoFrameworks) => {
380
+ const preset = requestedPreset ?? detected.preset;
381
+ const presetDefaults = preset ? resolvePreset(preset) : {};
382
+ const frameworkDefaults = autoFrameworks ? createDetectedFrameworkFlags(detected.detectedFrameworks) : {};
383
+ return { frameworkDefaults, preset, presetDefaults };
384
+ };
385
+ const resolveConfiguredProjects = (detected, options) => ({
386
+ ...detected.projects ?? {},
387
+ ...options?.projects ?? {}
388
+ });
389
+ const resolveBucketDefaults = (detected) => ({
390
+ detectedExtensions: detected.extensions ?? [],
391
+ detectedFormats: detected.formats ?? [],
392
+ detectedLibraries: detected.libraries ?? [],
393
+ detectedTesting: detected.testing ?? [],
394
+ detectedTools: detected.tools ?? []
395
+ });
396
+ const resolveNextModeValue = (options, presetDefaults, detected) => options?.nextMode ?? presetDefaults.nextMode ?? detected.nextMode ?? NextMode.Pages;
397
+ const resolveRuntimeValue = (options, presetDefaults, detected) => options?.runtime ?? presetDefaults.runtime ?? detected.runtime ?? Runtime.Universal;
398
+ const resolveSettingsValue = (options, detected) => options?.settings ?? detected.settings ?? [];
399
+ const resolveTypescriptValue = (options, presetDefaults, detected) => options?.typescript ?? presetDefaults.typescript ?? detected.typescript ?? false;
400
+ const needsReactAutoAdd = (frameworks) => Boolean(
401
+ frameworks.next ?? frameworks.expo ?? frameworks.remix ?? frameworks["react-router"] ?? (frameworks["tanstack-start"] && !frameworks.solid)
402
+ ) && !frameworks.react;
403
+ const applyFrameworkImpliedDeps = (frameworks) => {
404
+ const result = { ...frameworks };
405
+ if (needsReactAutoAdd(result)) result.react = true;
406
+ if (result.slidev && !result.vue) result.vue = true;
407
+ if (result.nuxt && !result.vue) result.vue = true;
408
+ return result;
409
+ };
410
+ const buildIgnoresConfig = (useDefaultIgnores, useGeneratedCodeIgnores, useGitignore, options) => ({
411
+ defaultIgnores: useDefaultIgnores ? [{
412
+ ignores: [...DEFAULT_IGNORES, ...useGeneratedCodeIgnores ? GENERATED_CODE_IGNORES : []],
413
+ name: "eslint-config-basic/default-ignores"
414
+ }] : [],
415
+ gitignoreConfig: useGitignore ? gitignore : [],
416
+ userIgnores: options?.ignores?.length ? [{
417
+ ignores: options.ignores,
418
+ name: "eslint-config-basic/ignores"
419
+ }] : []
420
+ });
421
+ const buildEslintConfigs = async (params) => {
422
+ const {
423
+ astroOptions,
424
+ defaultIgnores,
425
+ gitignoreConfig,
426
+ nextMode,
427
+ resolvedFrameworks,
428
+ resolvedTypescript,
429
+ rootDir: _rootDir,
430
+ runtime,
431
+ runtimeCoreConfig,
432
+ tailwindEntryPoint,
433
+ tsconfigRootDir,
434
+ uniqueExtensions,
435
+ uniqueFormats,
436
+ uniqueLibraries,
437
+ uniqueTesting,
438
+ uniqueTools,
439
+ userIgnores
440
+ } = params;
441
+ const { hasReact, hasSolid, hasSvelte, hasVue } = astroOptions;
442
+ const [
443
+ reactParam,
444
+ nextParam,
445
+ astroParam,
446
+ expoParam,
447
+ nestParam,
448
+ honoParam,
449
+ vueParam,
450
+ svelteParam,
451
+ solidParam,
452
+ angularParam,
453
+ qwikParam,
454
+ remixParam,
455
+ reactRouterParam,
456
+ tanstackStartParam,
457
+ nuxtParam,
458
+ preactParam,
459
+ litParam,
460
+ slidevParam,
461
+ viteParam
462
+ ] = await Promise.all([
463
+ resolveFramework("react", resolvedFrameworks.react),
464
+ resolveFramework("next", resolvedFrameworks.next),
465
+ resolveFramework("astro", resolvedFrameworks.astro, { hasReact, hasSolid, hasSvelte, hasVue }),
466
+ resolveFramework("expo", resolvedFrameworks.expo),
467
+ resolveFramework("nest", resolvedFrameworks.nest),
468
+ resolveFramework("hono", resolvedFrameworks.hono, { runtime }),
469
+ resolveFramework("vue", resolvedFrameworks.vue),
470
+ resolveFramework("svelte", resolvedFrameworks.svelte),
471
+ resolveFramework("solid", resolvedFrameworks.solid),
472
+ resolveFramework("angular", resolvedFrameworks.angular),
473
+ resolveFramework("qwik", resolvedFrameworks.qwik),
474
+ resolveFramework("remix", resolvedFrameworks.remix),
475
+ resolveFramework("react-router", resolvedFrameworks["react-router"]),
476
+ resolveFramework("tanstack-start", resolvedFrameworks["tanstack-start"]),
477
+ resolveFramework("nuxt", resolvedFrameworks.nuxt),
478
+ resolveFramework("preact", resolvedFrameworks.preact),
479
+ resolveFramework("lit", resolvedFrameworks.lit),
480
+ resolveFramework("slidev", resolvedFrameworks.slidev, { runtime }),
481
+ resolveFramework("vite", resolvedFrameworks.vite, { runtime })
482
+ ]);
483
+ return [
484
+ ...defaultIgnores,
485
+ ...userIgnores,
486
+ ...gitignoreConfig,
124
487
  ...tsconfigRootDir ? [{
125
- name: "eslint-config-basic/tsconfig-root-dir",
126
- languageOptions: {
127
- parserOptions: {
128
- tsconfigRootDir
129
- }
130
- }
488
+ languageOptions: { parserOptions: { tsconfigRootDir } },
489
+ name: "eslint-config-basic/tsconfig-root-dir"
131
490
  }] : [],
132
- // Core JS config with runtime-aware globals
491
+ {
492
+ files: ["**/*.cjs", "**/*.cts"],
493
+ languageOptions: {
494
+ globals: {
495
+ __dirname: "readonly",
496
+ __filename: "readonly",
497
+ exports: "readonly",
498
+ module: "readonly",
499
+ require: "readonly"
500
+ },
501
+ sourceType: "commonjs"
502
+ },
503
+ name: "eslint-config-basic/commonjs"
504
+ },
133
505
  ...runtimeCoreConfig,
134
- // React config (included if any React-based framework is used/passed)
135
506
  ...hasReact ? reactParam : [],
136
507
  ...nextParam,
137
508
  ...expoParam,
@@ -143,82 +514,310 @@ const eslintConfig = (options) => {
143
514
  ...angularParam,
144
515
  ...qwikParam,
145
516
  ...remixParam,
146
- ...typescript ? createTypescriptConfig({ tsconfigRootDir }) : [],
147
- // Astro needs to run after generic TypeScript so its parser and false-positive
148
- // workarounds win for .astro files and embedded expressions.
517
+ ...reactRouterParam,
518
+ ...tanstackStartParam,
519
+ ...nuxtParam,
520
+ ...preactParam,
521
+ ...litParam,
522
+ ...slidevParam,
523
+ ...viteParam,
524
+ ...resolvedTypescript ? createTypescriptConfig({
525
+ ...resolvedTypescript,
526
+ tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
527
+ }) : [],
149
528
  ...astroParam,
150
- // Next.js App Router overrides (#12)
151
- ...frameworks.next && nextMode === NextMode.AppRouter ? [
152
- {
153
- name: "eslint-config-next/app-router-overrides",
154
- files: ["app/**/*.{ts,tsx}"],
155
- rules: {
156
- "@next/next/no-html-link-for-pages": "off"
529
+ ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [{
530
+ files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
531
+ name: "eslint-config-next/app-router-overrides",
532
+ rules: { "@next/next/no-html-link-for-pages": "off" }
533
+ }] : [],
534
+ ...await getIntegrationConfigs(uniqueLibraries, uniqueTools, uniqueTesting, uniqueFormats, uniqueExtensions),
535
+ ...tailwindEntryPoint ? [{
536
+ name: "eslint-config-basic/tailwind-settings",
537
+ settings: {
538
+ "better-tailwindcss": {
539
+ detectComponentClasses: true,
540
+ entryPoint: tailwindEntryPoint
157
541
  }
158
542
  }
159
- ] : [],
160
- // Optionals
161
- ...getOptionalConfigs(
162
- uniqueLibraries,
163
- uniqueTools,
164
- uniqueTesting,
165
- uniqueFormats,
166
- uniqueExtensions
167
- ),
168
- // Prettier always last
169
- ...getPrettierConfig(uniqueTools)
543
+ }] : [],
544
+ ...await getPrettierConfig(uniqueTools)
170
545
  ];
171
- return applyStrictMode(configs, strict);
172
546
  };
547
+ const logEslintDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStrategy, preset, resolvedFrameworks, resolvedTypescript, runtime, tsconfigRootDir, uniqueExtensions, uniqueFormats, uniqueLibraries, uniqueTesting, uniqueTools) => {
548
+ if (!process.env.ESLINT_BASIC_DEBUG) return;
549
+ process.stdout.write(`[ESLint Basic] Resolved options: ${JSON.stringify({
550
+ autoFrameworks,
551
+ detectRootDir: detectRootDir ?? process.cwd(),
552
+ extensions: uniqueExtensions,
553
+ formats: uniqueFormats,
554
+ frameworks: Object.entries(resolvedFrameworks).filter(([, value]) => Boolean(value)).map(([key]) => key),
555
+ libraries: uniqueLibraries,
556
+ nextMode,
557
+ optionMergeStrategy,
558
+ preset,
559
+ runtime,
560
+ testing: uniqueTesting,
561
+ tools: uniqueTools,
562
+ tsconfigRootDir,
563
+ typescript: resolvedTypescript ? resolvedTypescript.mode : false
564
+ }, null, 2)}
565
+ `);
566
+ };
567
+ const eslintConfig = async (options) => {
568
+ const {
569
+ detection,
570
+ extensions: optExtensions,
571
+ formats: optFormats,
572
+ frameworks: optFrameworks,
573
+ libraries: optLibraries,
574
+ strict: optStrict,
575
+ testing: optTesting,
576
+ tools: optTools,
577
+ tsconfigRootDir: optTsconfigRootDir,
578
+ workspacePrefixes
579
+ } = options ?? {};
580
+ const { autoFrameworks, detectRootDir, optionMergeStrategy, requestedPreset } = resolveConfigSetup(options);
581
+ const shouldDefaultProjectDetection = requestedPreset === Preset.Monorepo;
582
+ const detected = applyDetectionControls(
583
+ detectProjectOptions(detectRootDir),
584
+ detection,
585
+ { projects: shouldDefaultProjectDetection }
586
+ );
587
+ const { frameworkDefaults, preset, presetDefaults } = resolvePresetMeta(requestedPreset, detected, autoFrameworks);
588
+ const configuredProjects = resolveConfiguredProjects(detected, options);
589
+ const { detectedExtensions, detectedFormats, detectedLibraries, detectedTesting, detectedTools } = resolveBucketDefaults(detected);
590
+ const configuredExtensions = mergeOptionalBucket(
591
+ "extensions",
592
+ detectedExtensions,
593
+ presetDefaults.extensions,
594
+ optExtensions,
595
+ options,
596
+ optionMergeStrategy
597
+ );
598
+ const formats = mergeOptionalBucket(
599
+ "formats",
600
+ detectedFormats,
601
+ presetDefaults.formats,
602
+ optFormats,
603
+ options,
604
+ optionMergeStrategy
605
+ );
606
+ const frameworks = mergeFrameworkOption(frameworkDefaults, presetDefaults.frameworks, optFrameworks, optionMergeStrategy);
607
+ const libraries = mergeOptionalBucket(
608
+ "libraries",
609
+ detectedLibraries,
610
+ presetDefaults.libraries,
611
+ optLibraries,
612
+ options,
613
+ optionMergeStrategy
614
+ );
615
+ const nextMode = resolveNextModeValue(options, presetDefaults, detected);
616
+ const runtime = resolveRuntimeValue(options, presetDefaults, detected);
617
+ const settings = resolveSettingsValue(options, detected);
618
+ const strict = getStrictMode(optStrict, presetDefaults.strict);
619
+ const testing = mergeOptionalBucket(
620
+ "testing",
621
+ detectedTesting,
622
+ presetDefaults.testing,
623
+ optTesting,
624
+ options,
625
+ optionMergeStrategy
626
+ );
627
+ const tools = mergeOptionalBucket(
628
+ "tools",
629
+ detectedTools,
630
+ presetDefaults.tools,
631
+ optTools,
632
+ options,
633
+ optionMergeStrategy
634
+ );
635
+ const typescript = resolveTypescriptValue(options, presetDefaults, detected);
636
+ const resolvedTypescript = resolveTypescriptOptions(typescript);
637
+ const rootDir = detectRootDir ?? process.cwd();
638
+ const tsconfigRootDir = resolveTsconfigRootDir(rootDir, typescript, optTsconfigRootDir);
639
+ const extensions = applyStrictProfileDefaults(configuredExtensions, strict);
640
+ const resolvedFrameworks = applyFrameworkImpliedDeps({ ...frameworks });
641
+ const uniqueLibraries = [...new Set(libraries)];
642
+ const uniqueTesting = [...new Set(testing)];
643
+ const uniqueFormats = [...new Set(formats)];
644
+ const uniqueTools = [...new Set(tools)];
645
+ const uniqueExtensions = [...new Set(extensions)];
646
+ const uniqueSettings = [...new Set(settings)];
647
+ const hasReact = !!resolvedFrameworks.react;
648
+ const hasVue = !!resolvedFrameworks.vue;
649
+ const hasSvelte = !!resolvedFrameworks.svelte;
650
+ const hasSolid = !!resolvedFrameworks.solid;
651
+ const useGitignore = !uniqueSettings.includes(Setting.NoGitignore);
652
+ const useDefaultIgnores = !uniqueSettings.includes(Setting.NoDefaultIgnores);
653
+ const useGeneratedCodeIgnores = !uniqueSettings.includes(Setting.NoGeneratedCodeIgnores);
654
+ const tailwindEntryPoint = uniqueLibraries.includes(Library.Tailwind) ? findTailwindEntryPoint(rootDir) : void 0;
655
+ const runtimeCoreConfig = runtime === Runtime.Universal ? coreConfig : createCoreConfig(runtime);
656
+ const { defaultIgnores, gitignoreConfig, userIgnores } = buildIgnoresConfig(useDefaultIgnores, useGeneratedCodeIgnores, useGitignore, options);
657
+ const configs = await buildEslintConfigs({
658
+ astroOptions: { hasReact, hasSolid, hasSvelte, hasVue },
659
+ defaultIgnores,
660
+ gitignoreConfig,
661
+ nextMode,
662
+ resolvedFrameworks,
663
+ resolvedTypescript,
664
+ rootDir,
665
+ runtime,
666
+ runtimeCoreConfig,
667
+ tailwindEntryPoint,
668
+ tsconfigRootDir,
669
+ uniqueExtensions,
670
+ uniqueFormats,
671
+ uniqueLibraries,
672
+ uniqueTesting,
673
+ uniqueTools,
674
+ userIgnores
675
+ });
676
+ logEslintDebug(
677
+ autoFrameworks,
678
+ detectRootDir,
679
+ nextMode,
680
+ optionMergeStrategy,
681
+ preset,
682
+ resolvedFrameworks,
683
+ resolvedTypescript,
684
+ runtime,
685
+ tsconfigRootDir,
686
+ uniqueExtensions,
687
+ uniqueFormats,
688
+ uniqueLibraries,
689
+ uniqueTesting,
690
+ uniqueTools
691
+ );
692
+ const projectConfigs = await Promise.all(
693
+ Object.entries(configuredProjects).map(async ([projectPath, projectOptions]) => {
694
+ const projectRoot = join(detectRootDir ?? process.cwd(), projectPath);
695
+ const scopedConfigs = await eslintConfig({
696
+ ...projectOptions,
697
+ detectRootDir: projectOptions.detectRootDir ?? projectRoot,
698
+ projects: void 0,
699
+ tsconfigRootDir: projectOptions.tsconfigRootDir ?? projectRoot
700
+ });
701
+ return scopedConfigs.map((config) => scopeConfigToProject(config, projectPath));
702
+ })
703
+ );
704
+ const allConfigs = [...configs, ...projectConfigs.flat()];
705
+ const patchedConfigs = workspacePrefixes?.length ? patchImportGroups(allConfigs, workspacePrefixes) : allConfigs;
706
+ return applyStrictMode(patchedConfigs, strict);
707
+ };
708
+ const defineConfig = eslintConfig;
173
709
  export {
174
710
  AGENT_TARGETS,
175
- Extension,
176
- Format,
177
- Library,
711
+ Extension2 as Extension,
712
+ Format2 as Format,
713
+ Library2 as Library,
178
714
  NextMode2 as NextMode,
179
- Preset,
715
+ Preset2 as Preset,
180
716
  ReactConfigKeys,
181
717
  Runtime2 as Runtime,
182
718
  Setting2 as Setting,
183
- Testing,
184
- Tool,
719
+ Testing2 as Testing,
720
+ Tool2 as Tool,
721
+ a11y,
722
+ aiSdk,
723
+ angular,
724
+ angularConfig,
725
+ astro,
726
+ autogen,
185
727
  bestPractices,
728
+ biome,
729
+ command,
730
+ compat,
186
731
  coreConfig2 as coreConfig,
187
732
  createCoreConfig2 as createCoreConfig,
733
+ createImportGroups,
188
734
  cspell,
735
+ css,
189
736
  cypress,
737
+ deMorgan,
738
+ defineConfig,
739
+ depend,
190
740
  detectProjectOptions2 as detectProjectOptions,
741
+ docker,
742
+ drizzle,
191
743
  eslintConfig,
744
+ expo,
745
+ expoConfig,
192
746
  generateAgentSkills,
193
747
  generateSkillContent,
194
748
  getGlobalsForRuntime,
749
+ githubActions,
195
750
  gitignore2 as gitignore,
751
+ googleGenAi,
196
752
  graphql,
753
+ groups,
197
754
  hasReactConfig,
755
+ hono,
756
+ html,
198
757
  i18next,
199
758
  jest,
759
+ jestDom,
200
760
  jsConfig,
201
761
  jsdoc,
202
762
  jsonc,
763
+ langchain,
764
+ lit,
765
+ llamaIndex,
203
766
  markdown,
767
+ mastra,
768
+ mcp,
204
769
  mdx,
770
+ mikroOrm,
771
+ nest,
772
+ nestConfig,
773
+ next,
774
+ nextConfig,
775
+ noOnlyTests,
776
+ node,
777
+ nuxt,
778
+ nx,
779
+ openAiAgents,
780
+ oxlint,
781
+ packageJson,
205
782
  perfectionist,
206
783
  playwright,
784
+ pnpm,
785
+ preact,
786
+ preactConfig,
207
787
  prettier,
788
+ prisma,
789
+ qwik,
790
+ react,
791
+ reactConfig,
792
+ reactRouter,
208
793
  regexp,
794
+ remix,
209
795
  security,
796
+ sequelize,
797
+ slidev,
798
+ solid,
799
+ solidConfig,
210
800
  sonarjs,
211
801
  stencil,
212
802
  storybook,
803
+ svelte,
804
+ svelteConfig,
213
805
  swagger,
214
806
  tailwind,
215
807
  tanstackQuery,
216
808
  tanstackRouter,
809
+ tanstackStart,
217
810
  testingLibrary,
218
811
  toml,
219
812
  tsConfig,
813
+ turbo,
814
+ typeorm,
220
815
  typescriptConfig,
221
816
  unicorn,
817
+ vite,
222
818
  vitest,
223
- yaml
819
+ vue,
820
+ vueConfig,
821
+ yaml,
822
+ zod
224
823
  };