@santi020k/eslint-config-lite 2.0.0-beta.0 → 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,33 @@
1
1
  # @santi020k/eslint-config-lite
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-typescript@2.0.0-beta.3
10
+
11
+ ## 2.0.0-beta.2
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies []:
16
+ - @santi020k/eslint-config-core@2.0.0-beta.2
17
+ - @santi020k/eslint-config-typescript@2.0.0-beta.2
18
+
19
+ ## 2.0.0-beta.1
20
+
21
+ ### Minor Changes
22
+
23
+ - [`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
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [[`dfba51d`](https://github.com/santi020k/eslint-config-basic/commit/dfba51dca7387a71a263af206eb86fd8df15f387)]:
28
+ - @santi020k/eslint-config-core@2.0.0-beta.1
29
+ - @santi020k/eslint-config-typescript@2.0.0-beta.1
30
+
3
31
  ## 2.0.0-beta.0
4
32
 
5
33
  ### 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));
2
5
  const createDetectedFrameworkFlags = (detectedFrameworks = []) => Object.fromEntries(
3
6
  detectedFrameworks.map((framework) => [framework, true])
4
7
  );
@@ -30,6 +33,7 @@ const loadFrameworkConfigInput = (frameworkName) => {
30
33
  const loader = frameworkLoaders.get(frameworkName);
31
34
  if (!loader) return Promise.resolve([]);
32
35
  const pending = loader().catch((error) => {
36
+ frameworkConfigCache.delete(frameworkName);
33
37
  throw new Error(
34
38
  `Unable to load optional framework config "${frameworkName}". Install "@santi020k/eslint-config-${frameworkName}" when using @santi020k/eslint-config-lite, or remove that framework from your eslintConfig options.`,
35
39
  { cause: error }
package/dist/index.js CHANGED
@@ -1,25 +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
- Extension,
8
- Format,
10
+ findTailwindEntryPoint,
11
+ GENERATED_CODE_IGNORES,
12
+ getStrictMode,
9
13
  gitignore,
10
14
  Library,
15
+ mergeFrameworkOption,
16
+ mergeOptionalBucket,
11
17
  NextMode,
18
+ patchImportGroups,
12
19
  Preset,
20
+ resolveTsconfigRootDir,
21
+ resolveTypescriptOptions,
13
22
  Runtime,
14
- Setting,
15
- Testing,
16
- Tool
23
+ scopeConfigToProject,
24
+ Setting
17
25
  } from "@santi020k/eslint-config-core";
18
26
  import { createTypescriptConfig } from "@santi020k/eslint-config-typescript";
19
- import { applyStrictMode } from "./compose.js";
20
27
  import { createDetectedFrameworkFlags } from "./frameworks.js";
21
28
  import { getIntegrationConfigs, getPrettierConfig } from "./integrations.js";
22
29
  import { resolveFramework, resolvePreset } from "./resolvers.js";
30
+ import { buildTailwindSettingsConfig } from "./tailwind.js";
23
31
  import {
24
32
  angular,
25
33
  angularConfig,
@@ -55,8 +63,8 @@ import {
55
63
  createCoreConfig as createCoreConfig2,
56
64
  createImportGroups,
57
65
  detectProjectOptions as detectProjectOptions2,
58
- Extension as Extension2,
59
- Format as Format2,
66
+ Extension,
67
+ Format,
60
68
  getGlobalsForRuntime,
61
69
  gitignore as gitignore2,
62
70
  hasReactConfig,
@@ -67,249 +75,10 @@ import {
67
75
  ReactConfigKeys,
68
76
  Runtime as Runtime2,
69
77
  Setting as Setting2,
70
- Testing as Testing2,
71
- Tool as Tool2
78
+ Testing,
79
+ Tool
72
80
  } from "@santi020k/eslint-config-core";
73
81
  import { tsConfig, typescriptConfig } from "@santi020k/eslint-config-typescript";
74
- const toUniqueArray = (values) => [...new Set(values)];
75
- const DEFAULT_IGNORES = [
76
- "**/dist/**",
77
- "**/build/**",
78
- "**/coverage/**",
79
- "**/.turbo/**",
80
- "**/.next/**",
81
- "**/.open-next/**",
82
- "**/.astro/**",
83
- "**/.svelte-kit/**",
84
- "**/.vercel/**",
85
- "**/.wrangler/**",
86
- "**/playwright-report/**",
87
- "**/test-results/**",
88
- "**/node_modules/**",
89
- "**/tsconfig.tsbuildinfo",
90
- // Package manager lock files (machine-generated, slow to lint)
91
- "**/pnpm-lock.yaml",
92
- "**/yarn.lock",
93
- "**/package-lock.json",
94
- "**/bun.lock",
95
- "**/bun.lockb",
96
- // AI coding-assistant artifacts (rule/skill folders managed by tools, not source code)
97
- "**/.agent/**",
98
- "**/.agents/**",
99
- "**/.aider*",
100
- "**/.aider*/**",
101
- "**/.claude/**",
102
- "**/.clinerules/**",
103
- "**/.codex/**",
104
- "**/.copilot/**",
105
- "**/.cursor/**",
106
- "**/.gemini/**",
107
- "**/.kiro/**",
108
- "**/.opencode/**",
109
- "**/.roo/**",
110
- "**/.windsurf/**"
111
- ];
112
- const GENERATED_CODE_IGNORES = [
113
- "**/__generated__/**",
114
- "**/generated/**",
115
- "**/codegen/**",
116
- "**/*.generated.*",
117
- "**/*.gen.*",
118
- "**/graphql.ts",
119
- "**/graphql.tsx",
120
- "**/gql/**",
121
- "**/.prisma/**"
122
- ];
123
- const TAILWIND_ENTRYPOINT_CANDIDATES = [
124
- "src/styles/global.css",
125
- "src/app/globals.css",
126
- "src/globals.css",
127
- "src/index.css",
128
- "app/globals.css",
129
- "styles/global.css"
130
- ];
131
- const OPTIONAL_BUCKETS = {
132
- extensions: Object.values(Extension),
133
- formats: Object.values(Format),
134
- libraries: Object.values(Library),
135
- testing: Object.values(Testing),
136
- tools: Object.values(Tool)
137
- };
138
- const mergeArrayOption = (detectedValues, presetValues, explicitValues, strategy) => {
139
- if (strategy === "replace") {
140
- if (explicitValues) return toUniqueArray(explicitValues);
141
- if (presetValues) return toUniqueArray(presetValues);
142
- return toUniqueArray(detectedValues);
143
- }
144
- return toUniqueArray([
145
- ...detectedValues,
146
- ...presetValues ?? [],
147
- ...explicitValues ?? []
148
- ]);
149
- };
150
- const isOptionalBucketValue = (bucket, value) => OPTIONAL_BUCKETS[bucket].includes(value);
151
- const getFeatureEntries = (options, bucket, enabled) => [
152
- ...Object.entries(options?.features ?? {}),
153
- ...Object.entries(options?.integrations ?? {})
154
- ].filter(([name, value]) => value === enabled && isOptionalBucketValue(bucket, name)).map(([name]) => name);
155
- const applyFeatureDisables = (values, options, bucket) => {
156
- const disabled = new Set(getFeatureEntries(options, bucket, false));
157
- return values.filter((value) => !disabled.has(value));
158
- };
159
- const mergeOptionalBucket = (bucket, detectedValues, presetValues, explicitValues, options, strategy) => {
160
- const featureEntries = getFeatureEntries(options, bucket, true);
161
- const hasExplicitInput = explicitValues !== void 0 || featureEntries.length > 0;
162
- const mergedExplicit = hasExplicitInput ? [...explicitValues ?? [], ...featureEntries] : void 0;
163
- return applyFeatureDisables(
164
- mergeArrayOption(detectedValues, presetValues, mergedExplicit, strategy),
165
- options,
166
- bucket
167
- );
168
- };
169
- const mergeFrameworkOption = (detectedFrameworks, presetFrameworks, explicitFrameworks, strategy) => {
170
- if (strategy === "replace") {
171
- if (explicitFrameworks) return { ...explicitFrameworks };
172
- if (presetFrameworks) return { ...presetFrameworks };
173
- return { ...detectedFrameworks };
174
- }
175
- return {
176
- ...detectedFrameworks,
177
- ...presetFrameworks ?? {},
178
- ...explicitFrameworks ?? {}
179
- };
180
- };
181
- const resolveDetectionOptions = (detection, defaults = {}) => {
182
- const defaultControls = {
183
- extensions: true,
184
- formats: true,
185
- frameworks: true,
186
- libraries: true,
187
- nextMode: true,
188
- projects: false,
189
- runtime: true,
190
- testing: true,
191
- tools: true,
192
- typescript: true,
193
- ...defaults
194
- };
195
- if (detection === false) {
196
- return Object.fromEntries(
197
- Object.keys(defaultControls).map((key) => [key, false])
198
- );
199
- }
200
- if (detection === true || detection === void 0) {
201
- return defaultControls;
202
- }
203
- return {
204
- ...defaultControls,
205
- ...detection
206
- };
207
- };
208
- const applyArrayDetectionControls = (controls, detected) => ({
209
- detectedFrameworks: controls.frameworks ? detected.detectedFrameworks : [],
210
- extensions: controls.extensions ? detected.extensions : [],
211
- formats: controls.formats ? detected.formats : [],
212
- libraries: controls.libraries ? detected.libraries : [],
213
- testing: controls.testing ? detected.testing : [],
214
- tools: controls.tools ? detected.tools : []
215
- });
216
- const applyScalarDetectionControls = (controls, detected) => ({
217
- nextMode: controls.nextMode ? detected.nextMode : void 0,
218
- preset: controls.typescript && controls.runtime ? detected.preset : void 0,
219
- projects: controls.projects ? detected.projects : void 0,
220
- runtime: controls.runtime ? detected.runtime : void 0,
221
- typescript: controls.typescript ? detected.typescript : false
222
- });
223
- const applyDetectionControls = (detected, detection, defaults) => {
224
- const controls = resolveDetectionOptions(detection, defaults);
225
- return {
226
- ...detected,
227
- ...applyArrayDetectionControls(controls, detected),
228
- ...applyScalarDetectionControls(controls, detected)
229
- };
230
- };
231
- const getStrictMode = (explicitStrict, presetStrict) => explicitStrict ?? presetStrict ?? false;
232
- const applyStrictProfileDefaults = (extensions, strict) => {
233
- if (strict !== "pedantic") return extensions;
234
- return toUniqueArray([...extensions, Extension.BestPractices]);
235
- };
236
- const hasTsconfig = (rootDir) => [
237
- "tsconfig.eslint.json",
238
- "tsconfig.json",
239
- "tsconfig.base.json",
240
- "tsconfig.app.json",
241
- "tsconfig.node.json"
242
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- config root is user-selected project context
243
- ].some((fileName) => existsSync(join(rootDir, fileName)));
244
- const resolveTypescriptOptions = (typescript) => {
245
- if (!typescript || typescript === "off") return false;
246
- if (typescript === true) return { mode: "type-aware" };
247
- if (typeof typescript === "string") return { mode: typescript };
248
- if (typescript.mode === "off") return false;
249
- return {
250
- ...typescript,
251
- mode: typescript.mode ?? "type-aware"
252
- };
253
- };
254
- const resolveTsconfigRootDir = (rootDir, typescript, explicitRootDir) => {
255
- if (explicitRootDir) return explicitRootDir;
256
- return resolveTypescriptOptions(typescript) && hasTsconfig(rootDir) ? rootDir : void 0;
257
- };
258
- const findTailwindEntryPoint = (rootDir) => TAILWIND_ENTRYPOINT_CANDIDATES.find(
259
- // eslint-disable-next-line security/detect-non-literal-fs-filename -- probes known stylesheet candidates under project root
260
- (candidate) => existsSync(join(rootDir, candidate))
261
- );
262
- const scopeFilePattern = (projectPath, pattern) => {
263
- if (typeof pattern === "string") {
264
- const isNegated = pattern.startsWith("!");
265
- const basePattern = (isNegated ? pattern.slice(1) : pattern).replace(/^\.\//, "");
266
- const scoped = `${projectPath.replace(/\/$/, "")}/${basePattern}`;
267
- return isNegated ? `!${scoped}` : scoped;
268
- }
269
- if (Array.isArray(pattern)) {
270
- return pattern.map((item) => scopeFilePattern(projectPath, item));
271
- }
272
- return pattern;
273
- };
274
- const scopeConfigToProject = (config, projectPath) => {
275
- if ("ignores" in config && !config.files) {
276
- return {
277
- ...config,
278
- ignores: config.ignores?.map((pattern) => scopeFilePattern(projectPath, pattern))
279
- };
280
- }
281
- return {
282
- ...config,
283
- files: Array.isArray(config.files) ? config.files.map((pattern) => scopeFilePattern(projectPath, pattern)) : [`${projectPath.replace(/\/$/, "")}/**/*`]
284
- };
285
- };
286
- const patchImportGroupsConfig = (config, workspacePatterns) => {
287
- const rule = config.rules?.["simple-import-sort/imports"];
288
- if (!Array.isArray(rule) || !rule[1]) return config;
289
- const ruleOpts = rule[1];
290
- const existingGroups = ruleOpts.groups ?? [];
291
- if (existingGroups.length === 0) return config;
292
- const externalIdx = existingGroups.findIndex((g) => g.some((p) => p.includes("^@?\\w")));
293
- const insertAt = externalIdx >= 0 ? externalIdx : existingGroups.length;
294
- return {
295
- ...config,
296
- rules: {
297
- ...config.rules,
298
- "simple-import-sort/imports": [
299
- rule[0],
300
- { ...ruleOpts, groups: [...existingGroups.slice(0, insertAt), workspacePatterns, ...existingGroups.slice(insertAt)] }
301
- ]
302
- }
303
- };
304
- };
305
- const patchImportGroups = (allConfigs, workspacePrefixes) => {
306
- const workspacePatterns = workspacePrefixes.map(
307
- (p) => `^${p.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&")}(/.*|$)`
308
- );
309
- return allConfigs.map(
310
- (item) => Array.isArray(item) ? item : patchImportGroupsConfig(item, workspacePatterns)
311
- );
312
- };
313
82
  const resolveLiteSetup = (options) => ({
314
83
  autoFrameworks: options?.autoFrameworks ?? true,
315
84
  detectRootDir: options?.detectRootDir ?? options?.tsconfigRootDir,
@@ -361,7 +130,22 @@ const buildLiteIgnoresConfig = (useDefaultIgnores, useGeneratedCodeIgnores, useG
361
130
  name: "eslint-config-lite/ignores"
362
131
  }] : []
363
132
  });
364
- const logLiteDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStrategy, preset, resolvedFrameworks, resolvedTypescript, runtime, tsconfigRootDir, uniqueExtensions, uniqueFormats, uniqueLibraries, uniqueTesting, uniqueTools) => {
133
+ const logLiteDebug = ({
134
+ autoFrameworks,
135
+ detectRootDir,
136
+ nextMode,
137
+ optionMergeStrategy,
138
+ preset,
139
+ resolvedFrameworks,
140
+ resolvedTypescript,
141
+ runtime,
142
+ tsconfigRootDir,
143
+ uniqueExtensions,
144
+ uniqueFormats,
145
+ uniqueLibraries,
146
+ uniqueTesting,
147
+ uniqueTools
148
+ }) => {
365
149
  if (!process.env.ESLINT_CONFIG_LITE_DEBUG && !process.env.ESLINT_BASIC_DEBUG) return;
366
150
  process.stdout.write(`[ESLint Config Lite] Resolved options: ${JSON.stringify({
367
151
  autoFrameworks,
@@ -381,56 +165,53 @@ const logLiteDebug = (autoFrameworks, detectRootDir, nextMode, optionMergeStrate
381
165
  }, null, 2)}
382
166
  `);
383
167
  };
168
+ const FRAMEWORK_EXTRA_OPTS = {
169
+ astro: (ctx) => ({ hasReact: ctx.hasReact, hasSolid: ctx.hasSolid, hasSvelte: ctx.hasSvelte, hasVue: ctx.hasVue }),
170
+ hono: (ctx) => ({ runtime: ctx.runtime }),
171
+ slidev: (ctx) => ({ runtime: ctx.runtime }),
172
+ vite: (ctx) => ({ runtime: ctx.runtime })
173
+ };
174
+ const resolveEnabledFrameworks = async (frameworks, ctx) => {
175
+ const entries = await Promise.all(
176
+ Object.entries(frameworks).filter((entry) => Boolean(entry[1])).map(
177
+ ([name, value]) => (
178
+ // eslint-disable-next-line security/detect-object-injection
179
+ resolveFramework(name, value, FRAMEWORK_EXTRA_OPTS[name]?.(ctx)).then((config) => [name, config])
180
+ )
181
+ )
182
+ );
183
+ return Object.fromEntries(entries);
184
+ };
384
185
  const buildLiteEslintConfigs = async (params) => {
385
186
  const {
386
- angularParam,
387
- astroOptions,
388
- astroParam,
389
187
  defaultIgnores,
390
- expoParam,
188
+ frameworkConfigs,
391
189
  gitignoreConfig,
392
- honoParam,
393
- litParam,
394
- nestParam,
190
+ hasReact,
395
191
  nextMode,
396
- nextParam,
397
- nuxtParam,
398
- preactParam,
399
- qwikParam,
400
- reactParam,
401
- reactRouterParam,
402
- remixParam,
403
192
  resolvedFrameworks,
404
193
  resolvedTypescript,
405
194
  runtimeCoreConfig,
406
- slidevParam,
407
- solidParam,
408
- svelteParam,
409
- tailwindEntryPoint,
410
- tanstackStartParam,
195
+ tailwindOptions,
411
196
  tsconfigRootDir,
412
197
  uniqueExtensions,
413
198
  uniqueFormats,
414
199
  uniqueLibraries,
415
200
  uniqueTesting,
416
201
  uniqueTools,
417
- userIgnores,
418
- viteParam,
419
- vueParam
202
+ userIgnores
420
203
  } = params;
421
- const { hasReact } = astroOptions;
204
+ const get = (name) => {
205
+ const entry = Object.entries(frameworkConfigs).find(([k]) => k === name);
206
+ return entry ? entry[1] : [];
207
+ };
208
+ const tailwindSettingsConfig = buildTailwindSettingsConfig(tailwindOptions);
422
209
  return [
423
210
  ...defaultIgnores,
424
211
  ...userIgnores,
425
- // Settings
426
212
  ...gitignoreConfig,
427
- // Global TSConfig Root Dir fix
428
213
  ...tsconfigRootDir ? [{
429
- languageOptions: {
430
- parserOptions: {
431
- tsconfigRootDir
432
- }
433
- },
214
+ languageOptions: { parserOptions: { tsconfigRootDir } },
434
215
  name: "eslint-config-lite/tsconfig-root-dir"
435
216
  }] : [],
436
217
  {
@@ -447,66 +228,51 @@ const buildLiteEslintConfigs = async (params) => {
447
228
  },
448
229
  name: "eslint-config-lite/commonjs"
449
230
  },
450
- // Core JS config with runtime-aware globals
451
231
  ...runtimeCoreConfig,
452
- // React config (included if any React-based framework is used/passed)
453
- ...hasReact ? reactParam : [],
454
- ...nextParam,
455
- ...expoParam,
456
- ...nestParam,
457
- ...honoParam,
458
- ...vueParam,
459
- ...svelteParam,
460
- ...solidParam,
461
- ...angularParam,
462
- ...qwikParam,
463
- ...remixParam,
464
- ...reactRouterParam,
465
- ...tanstackStartParam,
466
- ...nuxtParam,
467
- ...preactParam,
468
- ...litParam,
469
- ...slidevParam,
470
- ...viteParam,
232
+ ...hasReact ? get("react") : [],
233
+ ...get("next"),
234
+ ...get("expo"),
235
+ ...get("nest"),
236
+ ...get("hono"),
237
+ ...get("vue"),
238
+ ...get("svelte"),
239
+ ...get("solid"),
240
+ ...get("angular"),
241
+ ...get("qwik"),
242
+ ...get("remix"),
243
+ ...get("react-router"),
244
+ ...get("tanstack-start"),
245
+ ...get("nuxt"),
246
+ ...get("preact"),
247
+ ...get("lit"),
248
+ ...get("slidev"),
249
+ ...get("vite"),
471
250
  ...resolvedTypescript ? createTypescriptConfig({
472
251
  ...resolvedTypescript,
473
252
  tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
474
253
  }) : [],
475
- // Astro needs to run after generic TypeScript so its parser and false-positive
476
- // workarounds win for .astro files and embedded expressions.
477
- ...astroParam,
478
- // Next.js App Router overrides (#12)
479
- ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [
480
- {
481
- files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
482
- name: "eslint-config-next/app-router-overrides",
483
- rules: {
484
- "@next/next/no-html-link-for-pages": "off"
485
- }
486
- }
487
- ] : [],
488
- // Integrations
489
- ...await getIntegrationConfigs(
490
- uniqueLibraries,
491
- uniqueTools,
492
- uniqueTesting,
493
- uniqueFormats,
494
- uniqueExtensions
495
- ),
496
- ...tailwindEntryPoint ? [{
497
- name: "eslint-config-lite/tailwind-settings",
498
- settings: {
499
- "better-tailwindcss": {
500
- detectComponentClasses: true,
501
- entryPoint: tailwindEntryPoint
502
- }
503
- }
254
+ // Astro runs after TypeScript so its parser wins for .astro files and embedded expressions.
255
+ ...get("astro"),
256
+ ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [{
257
+ files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
258
+ name: "eslint-config-next/app-router-overrides",
259
+ rules: { "@next/next/no-html-link-for-pages": "off" }
504
260
  }] : [],
505
- // Prettier always last
261
+ ...await getIntegrationConfigs(uniqueLibraries, uniqueTools, uniqueTesting, uniqueFormats, uniqueExtensions),
262
+ ...tailwindSettingsConfig ? [tailwindSettingsConfig] : [],
506
263
  ...await getPrettierConfig(uniqueTools)
507
264
  ];
508
265
  };
266
+ const emitLiteWarnings = (options) => {
267
+ if (options?.frameworks && "remix" in options.frameworks) {
268
+ process.emitWarning('`frameworks.remix` is deprecated and will be removed in the next major. Please use `frameworks["react-router"]` instead.', "eslint-config-lite");
269
+ }
270
+ if (options?.typescript && typeof options.typescript === "object" && "project" in options.typescript) {
271
+ process.emitWarning("`typescript.project` is ignored in v2. Type-aware linting now relies on typescript-eslint projectService.", "eslint-config-lite");
272
+ }
273
+ };
509
274
  const eslintConfig = async (options) => {
275
+ emitLiteWarnings(options);
510
276
  const {
511
277
  detection,
512
278
  extensions: optExtensions,
@@ -519,6 +285,7 @@ const eslintConfig = async (options) => {
519
285
  runtime: optRuntime,
520
286
  settings: optSettings,
521
287
  strict: optStrict,
288
+ tailwind: optTailwind,
522
289
  testing: optTesting,
523
290
  tools: optTools,
524
291
  tsconfigRootDir: optTsconfigRootDir,
@@ -555,7 +322,12 @@ const eslintConfig = async (options) => {
555
322
  const tsconfigRootDir = resolveTsconfigRootDir(rootDir, typescript, optTsconfigRootDir);
556
323
  const extensions = applyStrictProfileDefaults(configuredExtensions, strict);
557
324
  const resolvedFrameworks = applyLiteFrameworkImpliedDeps({ ...frameworks });
558
- const uniqueLibraries = [...new Set(libraries)];
325
+ const uniqueLibraries = (() => {
326
+ const libs = [...new Set(libraries)];
327
+ if (optTailwind === false) return libs.filter((l) => l !== Library.Tailwind);
328
+ if (!optTailwind) return libs;
329
+ return [.../* @__PURE__ */ new Set([...libs, Library.Tailwind])];
330
+ })();
559
331
  const uniqueTesting = [...new Set(testing)];
560
332
  const uniqueFormats = [...new Set(formats)];
561
333
  const uniqueTools = [...new Set(tools)];
@@ -568,7 +340,19 @@ const eslintConfig = async (options) => {
568
340
  const useGitignore = !uniqueSettings.includes(Setting.NoGitignore);
569
341
  const useDefaultIgnores = !uniqueSettings.includes(Setting.NoDefaultIgnores);
570
342
  const useGeneratedCodeIgnores = !uniqueSettings.includes(Setting.NoGeneratedCodeIgnores);
571
- const tailwindEntryPoint = uniqueLibraries.includes(Library.Tailwind) ? findTailwindEntryPoint(rootDir) : void 0;
343
+ const tailwindOptions = (() => {
344
+ if (!uniqueLibraries.includes(Library.Tailwind)) return void 0;
345
+ const opts = typeof optTailwind === "object" ? optTailwind : {};
346
+ const entryPoint = opts.entryPoint ?? findTailwindEntryPoint(rootDir);
347
+ const hasCustomConfig = entryPoint || opts.detectComponentClasses !== void 0 || opts.ignore?.length || opts.noUnknownClasses !== void 0;
348
+ if (!hasCustomConfig) return void 0;
349
+ return {
350
+ detectComponentClasses: opts.detectComponentClasses ?? true,
351
+ ...entryPoint ? { entryPoint } : {},
352
+ ...opts.ignore?.length ? { ignore: opts.ignore } : {},
353
+ ...opts.noUnknownClasses === void 0 ? {} : { noUnknownClasses: opts.noUnknownClasses }
354
+ };
355
+ })();
572
356
  const runtimeCoreConfig = runtime === Runtime.Universal ? coreConfig : createCoreConfig(runtime);
573
357
  const { defaultIgnores, gitignoreConfig, userIgnores } = buildLiteIgnoresConfig(
574
358
  useDefaultIgnores,
@@ -576,86 +360,29 @@ const eslintConfig = async (options) => {
576
360
  useGitignore,
577
361
  optIgnores
578
362
  );
579
- const [
580
- reactParam,
581
- nextParam,
582
- astroParam,
583
- expoParam,
584
- nestParam,
585
- honoParam,
586
- vueParam,
587
- svelteParam,
588
- solidParam,
589
- angularParam,
590
- qwikParam,
591
- remixParam,
592
- reactRouterParam,
593
- tanstackStartParam,
594
- nuxtParam,
595
- preactParam,
596
- litParam,
597
- slidevParam,
598
- viteParam
599
- ] = await Promise.all([
600
- resolveFramework("react", resolvedFrameworks.react),
601
- resolveFramework("next", resolvedFrameworks.next),
602
- resolveFramework("astro", resolvedFrameworks.astro, { hasReact, hasSolid, hasSvelte, hasVue }),
603
- resolveFramework("expo", resolvedFrameworks.expo),
604
- resolveFramework("nest", resolvedFrameworks.nest),
605
- resolveFramework("hono", resolvedFrameworks.hono, { runtime }),
606
- resolveFramework("vue", resolvedFrameworks.vue),
607
- resolveFramework("svelte", resolvedFrameworks.svelte),
608
- resolveFramework("solid", resolvedFrameworks.solid),
609
- resolveFramework("angular", resolvedFrameworks.angular),
610
- resolveFramework("qwik", resolvedFrameworks.qwik),
611
- resolveFramework("remix", resolvedFrameworks.remix),
612
- resolveFramework("react-router", resolvedFrameworks["react-router"]),
613
- resolveFramework("tanstack-start", resolvedFrameworks["tanstack-start"]),
614
- resolveFramework("nuxt", resolvedFrameworks.nuxt),
615
- resolveFramework("preact", resolvedFrameworks.preact),
616
- resolveFramework("lit", resolvedFrameworks.lit),
617
- resolveFramework("slidev", resolvedFrameworks.slidev, { runtime }),
618
- resolveFramework("vite", resolvedFrameworks.vite, { runtime })
619
- ]);
363
+ const frameworkConfigs = await resolveEnabledFrameworks(
364
+ resolvedFrameworks,
365
+ { hasReact, hasSolid, hasSvelte, hasVue, runtime }
366
+ );
620
367
  const configs = await buildLiteEslintConfigs({
621
- angularParam,
622
- astroOptions: { hasReact, hasSolid, hasSvelte, hasVue },
623
- astroParam,
624
368
  defaultIgnores,
625
- expoParam,
369
+ frameworkConfigs,
626
370
  gitignoreConfig,
627
- honoParam,
628
- litParam,
629
- nestParam,
371
+ hasReact,
630
372
  nextMode,
631
- nextParam,
632
- nuxtParam,
633
- preactParam,
634
- qwikParam,
635
- reactParam,
636
- reactRouterParam,
637
- remixParam,
638
373
  resolvedFrameworks,
639
374
  resolvedTypescript,
640
- rootDir,
641
- runtime,
642
375
  runtimeCoreConfig,
643
- slidevParam,
644
- solidParam,
645
- svelteParam,
646
- tailwindEntryPoint,
647
- tanstackStartParam,
376
+ tailwindOptions,
648
377
  tsconfigRootDir,
649
378
  uniqueExtensions,
650
379
  uniqueFormats,
651
380
  uniqueLibraries,
652
381
  uniqueTesting,
653
382
  uniqueTools,
654
- userIgnores,
655
- viteParam,
656
- vueParam
383
+ userIgnores
657
384
  });
658
- logLiteDebug(
385
+ logLiteDebug({
659
386
  autoFrameworks,
660
387
  detectRootDir,
661
388
  nextMode,
@@ -670,7 +397,7 @@ const eslintConfig = async (options) => {
670
397
  uniqueLibraries,
671
398
  uniqueTesting,
672
399
  uniqueTools
673
- );
400
+ });
674
401
  const projectConfigs = await Promise.all(
675
402
  Object.entries(configuredProjects).map(async ([projectPath, projectOptions]) => {
676
403
  const projectRoot = join(detectRootDir ?? process.cwd(), projectPath);
@@ -689,16 +416,16 @@ const eslintConfig = async (options) => {
689
416
  };
690
417
  const defineConfig = eslintConfig;
691
418
  export {
692
- Extension2 as Extension,
693
- Format2 as Format,
419
+ Extension,
420
+ Format,
694
421
  Library2 as Library,
695
422
  NextMode2 as NextMode,
696
423
  Preset2 as Preset,
697
424
  ReactConfigKeys,
698
425
  Runtime2 as Runtime,
699
426
  Setting2 as Setting,
700
- Testing2 as Testing,
701
- Tool2 as Tool,
427
+ Testing,
428
+ Tool,
702
429
  angular,
703
430
  angularConfig,
704
431
  astro,
@@ -1,7 +1,8 @@
1
1
  import {
2
+ createModuleLoader,
2
3
  Tool
3
4
  } from "@santi020k/eslint-config-core";
4
- import { loadModule } from "./lazy.js";
5
+ const loadModule = createModuleLoader((specifier) => import.meta.resolve(specifier));
5
6
  const hasRequestedIntegrations = (libraries, tools, testing, formats, extensions) => libraries.length > 0 || tools.length > 0 || testing.length > 0 || formats.length > 0 || extensions.length > 0;
6
7
  let integrationsModule;
7
8
  const loadIntegrations = async () => {
package/dist/resolvers.js CHANGED
@@ -20,7 +20,7 @@ const resolveFramework = async (frameworkName, framework, options) => {
20
20
  if (typeof framework.default === "function") {
21
21
  return framework.default(options);
22
22
  }
23
- return framework.default;
23
+ if (Array.isArray(framework.default)) return framework.default;
24
24
  }
25
25
  if (Array.isArray(framework)) return framework;
26
26
  throw new TypeError(
@@ -0,0 +1,6 @@
1
+ import { TailwindOptions } from '@santi020k/eslint-config-core';
2
+ import { TSESLint } from '@typescript-eslint/utils';
3
+
4
+ declare const buildTailwindSettingsConfig: (tailwindOptions: TailwindOptions | undefined) => TSESLint.FlatConfig.Config | undefined;
5
+
6
+ export { buildTailwindSettingsConfig };
@@ -0,0 +1,27 @@
1
+ const buildTailwindSettingsConfig = (tailwindOptions) => {
2
+ if (!tailwindOptions) return void 0;
3
+ const { noUnknownClasses, ...settingsOptions } = tailwindOptions;
4
+ const unknownClassOptions = {};
5
+ if (tailwindOptions.entryPoint) unknownClassOptions.entryPoint = tailwindOptions.entryPoint;
6
+ if (tailwindOptions.ignore?.length) unknownClassOptions.ignore = tailwindOptions.ignore;
7
+ const hasUnknownClassOptions = Object.keys(unknownClassOptions).length > 0;
8
+ let noUnknownClassesRule;
9
+ if (hasUnknownClassOptions || noUnknownClasses !== void 0) {
10
+ const severity = noUnknownClasses ?? "error";
11
+ if (severity === false) {
12
+ noUnknownClassesRule = "off";
13
+ } else if (hasUnknownClassOptions) {
14
+ noUnknownClassesRule = [severity, unknownClassOptions];
15
+ } else {
16
+ noUnknownClassesRule = severity;
17
+ }
18
+ }
19
+ return {
20
+ name: "eslint-config-lite/tailwind-settings",
21
+ ...noUnknownClassesRule === void 0 ? {} : { rules: { "better-tailwindcss/no-unknown-classes": noUnknownClassesRule } },
22
+ settings: { "better-tailwindcss": settingsOptions }
23
+ };
24
+ };
25
+ export {
26
+ buildTailwindSettingsConfig
27
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@santi020k/eslint-config-lite",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.0.0-beta.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -23,57 +23,58 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "@typescript-eslint/utils": "^8.61.1",
26
- "@santi020k/eslint-config-core": "^2.0.0-beta.0",
27
- "@santi020k/eslint-config-typescript": "^2.0.0-beta.0"
26
+ "@santi020k/eslint-config-core": "^2.0.0-beta.3",
27
+ "@santi020k/eslint-config-typescript": "^2.0.0-beta.3"
28
28
  },
29
29
  "devDependencies": {
30
+ "@types/node": "^25.9.3",
30
31
  "publint": "^0.3.21",
31
32
  "tsup": "^8.5.1",
32
33
  "typescript": "^6.0.3",
33
- "@santi020k/eslint-config-angular": "^2.0.0-beta.0",
34
- "@santi020k/eslint-config-astro": "^2.0.0-beta.0",
35
- "@santi020k/eslint-config-expo": "^2.0.0-beta.0",
36
- "@santi020k/eslint-config-hono": "^2.0.0-beta.0",
37
- "@santi020k/eslint-config-integrations": "^2.0.0-beta.0",
38
- "@santi020k/eslint-config-lit": "^2.0.0-beta.0",
39
- "@santi020k/eslint-config-nest": "^2.0.0-beta.0",
40
- "@santi020k/eslint-config-next": "^2.0.0-beta.0",
41
- "@santi020k/eslint-config-nuxt": "^2.0.0-beta.0",
42
- "@santi020k/eslint-config-preact": "^2.0.0-beta.0",
43
- "@santi020k/eslint-config-qwik": "^2.0.0-beta.0",
44
- "@santi020k/eslint-config-react": "^2.0.0-beta.0",
45
- "@santi020k/eslint-config-react-router": "^2.0.0-beta.0",
46
- "@santi020k/eslint-config-remix": "^2.0.0-beta.0",
47
- "@santi020k/eslint-config-slidev": "^2.0.0-beta.0",
48
- "@santi020k/eslint-config-solid": "^2.0.0-beta.0",
49
- "@santi020k/eslint-config-svelte": "^2.0.0-beta.0",
50
- "@santi020k/eslint-config-tanstack-start": "^2.0.0-beta.0",
51
- "@santi020k/eslint-config-vite": "^2.0.0-beta.0",
52
- "@santi020k/eslint-config-vue": "^2.0.0-beta.0"
34
+ "@santi020k/eslint-config-angular": "^2.0.0-beta.3",
35
+ "@santi020k/eslint-config-astro": "^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-vite": "^2.0.0-beta.3",
53
+ "@santi020k/eslint-config-vue": "^2.0.0-beta.3"
53
54
  },
54
55
  "peerDependencies": {
55
56
  "eslint": "^10.0.0",
56
57
  "typescript": ">=5.0.0",
57
- "@santi020k/eslint-config-angular": "^2.0.0-beta.0",
58
- "@santi020k/eslint-config-astro": "^2.0.0-beta.0",
59
- "@santi020k/eslint-config-expo": "^2.0.0-beta.0",
60
- "@santi020k/eslint-config-hono": "^2.0.0-beta.0",
61
- "@santi020k/eslint-config-integrations": "^2.0.0-beta.0",
62
- "@santi020k/eslint-config-lit": "^2.0.0-beta.0",
63
- "@santi020k/eslint-config-nest": "^2.0.0-beta.0",
64
- "@santi020k/eslint-config-next": "^2.0.0-beta.0",
65
- "@santi020k/eslint-config-nuxt": "^2.0.0-beta.0",
66
- "@santi020k/eslint-config-preact": "^2.0.0-beta.0",
67
- "@santi020k/eslint-config-qwik": "^2.0.0-beta.0",
68
- "@santi020k/eslint-config-react": "^2.0.0-beta.0",
69
- "@santi020k/eslint-config-react-router": "^2.0.0-beta.0",
70
- "@santi020k/eslint-config-remix": "^2.0.0-beta.0",
71
- "@santi020k/eslint-config-slidev": "^2.0.0-beta.0",
72
- "@santi020k/eslint-config-solid": "^2.0.0-beta.0",
73
- "@santi020k/eslint-config-svelte": "^2.0.0-beta.0",
74
- "@santi020k/eslint-config-tanstack-start": "^2.0.0-beta.0",
75
- "@santi020k/eslint-config-vite": "^2.0.0-beta.0",
76
- "@santi020k/eslint-config-vue": "^2.0.0-beta.0"
58
+ "@santi020k/eslint-config-angular": "^2.0.0-beta.3",
59
+ "@santi020k/eslint-config-astro": "^2.0.0-beta.3",
60
+ "@santi020k/eslint-config-expo": "^2.0.0-beta.3",
61
+ "@santi020k/eslint-config-hono": "^2.0.0-beta.3",
62
+ "@santi020k/eslint-config-integrations": "^2.0.0-beta.3",
63
+ "@santi020k/eslint-config-lit": "^2.0.0-beta.3",
64
+ "@santi020k/eslint-config-nest": "^2.0.0-beta.3",
65
+ "@santi020k/eslint-config-next": "^2.0.0-beta.3",
66
+ "@santi020k/eslint-config-nuxt": "^2.0.0-beta.3",
67
+ "@santi020k/eslint-config-preact": "^2.0.0-beta.3",
68
+ "@santi020k/eslint-config-qwik": "^2.0.0-beta.3",
69
+ "@santi020k/eslint-config-react": "^2.0.0-beta.3",
70
+ "@santi020k/eslint-config-react-router": "^2.0.0-beta.3",
71
+ "@santi020k/eslint-config-remix": "^2.0.0-beta.3",
72
+ "@santi020k/eslint-config-slidev": "^2.0.0-beta.3",
73
+ "@santi020k/eslint-config-solid": "^2.0.0-beta.3",
74
+ "@santi020k/eslint-config-svelte": "^2.0.0-beta.3",
75
+ "@santi020k/eslint-config-tanstack-start": "^2.0.0-beta.3",
76
+ "@santi020k/eslint-config-vite": "^2.0.0-beta.3",
77
+ "@santi020k/eslint-config-vue": "^2.0.0-beta.3"
77
78
  },
78
79
  "peerDependenciesMeta": {
79
80
  "@santi020k/eslint-config-angular": {
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,30 +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
- return configs.map((config) => {
18
- if (config.rules) {
19
- const strictRules = Object.fromEntries(
20
- Object.entries(config.rules).map(([key, value]) => [key, promoteRuleSeverity(value)])
21
- );
22
- return { ...config, rules: strictRules };
23
- }
24
- return config;
25
- });
26
- };
27
- export {
28
- applyStrictMode,
29
- normalizeStrictMode
30
- };
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,9 +0,0 @@
1
- const isVitest = typeof process !== "undefined" && process.env.VITEST;
2
- const dynamicImport = isVitest ? (specifier) => import(
3
- /* @vite-ignore */
4
- specifier
5
- ) : Reflect.construct(Function, ["specifier", "return import(specifier)"]);
6
- const loadModule = async (specifier) => await dynamicImport(specifier);
7
- export {
8
- loadModule
9
- };