@santi020k/eslint-config-basic 3.1.0 → 3.2.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.d.ts CHANGED
@@ -1,8 +1,14 @@
1
- import { EslintConfigOptions, ConfigInput, FlatConfigArray } from '@santi020k/eslint-config-core';
1
+ import { FlatConfigArray, EslintConfigOptions, ConfigInput } 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, ProjectConfigOptions, ReactConfigKeys, Runtime, RuntimeName, RuntimeOption, Setting, SettingName, SettingOption, StrictMode, Testing, TestingName, TestingOption, Tool, ToolName, ToolOption, TypeScriptMode, TypeScriptOptions, coreConfig, createCoreConfig, createGitignoreConfig, createImportGroups, detectProjectOptions, getGlobalsForRuntime, groups, hasReactConfig } from '@santi020k/eslint-config-core';
3
3
  export { angular, astro, expo, hono, lit, nest, next, nuxt, preact, qwik, react, reactRouter, slidev, solid, svelte, tanstackStart, vite, vue } from './frameworks.js';
4
4
  export { typescriptConfig } from '@santi020k/eslint-config-typescript';
5
5
 
6
+ /**
7
+ * Copies already-loaded plugin objects onto rule blocks that reference them.
8
+ * ESLint 10 validates plugin availability per effective config object, while
9
+ * feature packs often keep plugin setup and consumer overrides separate.
10
+ */
11
+ declare const attachReferencedPlugins: (configs: FlatConfigArray) => FlatConfigArray;
6
12
  /**
7
13
  * Generates the ESLint configuration array, applying configurations
8
14
  * and integration settings based on the input configuration.
@@ -13,4 +19,4 @@ export { typescriptConfig } from '@santi020k/eslint-config-typescript';
13
19
  */
14
20
  declare const defineConfig: (options?: EslintConfigOptions, ...extraConfigs: ConfigInput[]) => Promise<FlatConfigArray>;
15
21
 
16
- export { defineConfig };
22
+ export { attachReferencedPlugins, defineConfig };
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  Testing
30
30
  } from "@santi020k/eslint-config-core";
31
31
  import { createTypescriptConfig } from "@santi020k/eslint-config-typescript";
32
+ import { attachDefineConfigMetadata } from "./define-config-metadata.js";
32
33
  import { createDetectedFrameworkFlags } from "./frameworks.js";
33
34
  import { getIntegrationConfigs, getPrettierConfig } from "./integrations.js";
34
35
  import { resolveFramework, resolvePreset } from "./resolvers.js";
@@ -266,6 +267,16 @@ const buildEslintConfigs = async (params) => {
266
267
  { hasReact, hasSolid, hasSvelte, hasVue, runtime, tsconfigRootDir }
267
268
  );
268
269
  const get = (name) => fw[name] ?? [];
270
+ const typescriptConfigs = resolvedTypescript ? createTypescriptConfig({
271
+ ...resolvedTypescript,
272
+ tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
273
+ }) : [];
274
+ const untypedTypescriptConfigs = typescriptConfigs.filter(
275
+ (config) => config.name === "eslint-config-typescript/untyped-files"
276
+ );
277
+ const typedTypescriptConfigs = typescriptConfigs.filter(
278
+ (config) => config.name !== "eslint-config-typescript/untyped-files"
279
+ );
269
280
  return [
270
281
  ...defaultIgnores,
271
282
  ...userIgnores,
@@ -306,10 +317,7 @@ const buildEslintConfigs = async (params) => {
306
317
  ...get("lit"),
307
318
  ...get("slidev"),
308
319
  ...get("vite"),
309
- ...resolvedTypescript ? createTypescriptConfig({
310
- ...resolvedTypescript,
311
- tsconfigRootDir: resolvedTypescript.tsconfigRootDir ?? tsconfigRootDir
312
- }) : [],
320
+ ...typedTypescriptConfigs,
313
321
  ...get("astro"),
314
322
  ...resolvedFrameworks.next && nextMode === NextMode.AppRouter ? [{
315
323
  files: ["app/**/*.{ts,tsx}", "src/**/*.{ts,tsx}"],
@@ -329,6 +337,7 @@ const buildEslintConfigs = async (params) => {
329
337
  }
330
338
  },
331
339
  ...tailwindOptions ? [buildTailwindSettingsConfig(tailwindOptions)] : [],
340
+ ...untypedTypescriptConfigs,
332
341
  ...await getPrettierConfig(uniqueTools)
333
342
  ];
334
343
  };
@@ -387,6 +396,52 @@ const resolveProjectConfigs = async (configuredProjects, projectDefaults, detect
387
396
  return scopedConfigs.map((config) => config.name === "eslint-config/gitignore" ? config : scopeConfigToProject(config, projectPath));
388
397
  })
389
398
  );
399
+ const resolveInheritedProjectDefaults = (options) => mergeProjectOptions(
400
+ {
401
+ ...options?.detection === void 0 ? {} : { detection: options.detection },
402
+ ...options?.tailwind === void 0 ? {} : { tailwind: options.tailwind }
403
+ },
404
+ options?.projectDefaults ?? {}
405
+ );
406
+ const getPluginNameFromRule = (ruleName, availablePluginNames) => {
407
+ const segments = ruleName.split("/");
408
+ if (segments.length < 2) return void 0;
409
+ const registeredPrefix = [...availablePluginNames].filter((pluginName) => ruleName.startsWith(`${pluginName}/`)).sort((left, right) => right.length - left.length)[0];
410
+ if (registeredPrefix) return registeredPrefix;
411
+ return ruleName.startsWith("@") && segments.length > 2 ? segments.slice(0, 2).join("/") : segments[0];
412
+ };
413
+ const attachReferencedPlugins = (configs) => {
414
+ const availablePlugins = /* @__PURE__ */ new Map();
415
+ const ambiguousPluginNames = /* @__PURE__ */ new Set();
416
+ for (const config of configs) {
417
+ for (const [pluginName, plugin] of Object.entries(config.plugins ?? {})) {
418
+ const existingPlugin = availablePlugins.get(pluginName);
419
+ if (existingPlugin && existingPlugin !== plugin) {
420
+ availablePlugins.delete(pluginName);
421
+ ambiguousPluginNames.add(pluginName);
422
+ } else if (!ambiguousPluginNames.has(pluginName)) {
423
+ availablePlugins.set(pluginName, plugin);
424
+ }
425
+ }
426
+ }
427
+ return configs.map((config) => {
428
+ const requiredPlugins = new Set(
429
+ Object.keys(config.rules ?? {}).map((ruleName) => getPluginNameFromRule(ruleName, availablePlugins.keys())).filter((pluginName) => pluginName !== void 0)
430
+ );
431
+ const missingPluginEntries = [...requiredPlugins].flatMap((pluginName) => {
432
+ const plugin = availablePlugins.get(pluginName);
433
+ return !Object.hasOwn(config.plugins ?? {}, pluginName) && plugin ? [[pluginName, plugin]] : [];
434
+ });
435
+ if (missingPluginEntries.length === 0) return config;
436
+ return {
437
+ ...config,
438
+ plugins: {
439
+ ...config.plugins,
440
+ ...Object.fromEntries(missingPluginEntries)
441
+ }
442
+ };
443
+ });
444
+ };
390
445
  const getConfigsParams = (options, rootDir, runtime, uniqueLibraries, uniqueSettings, resolvedFrameworks) => {
391
446
  const hasReact = !!resolvedFrameworks.react;
392
447
  const hasVue = !!resolvedFrameworks.vue;
@@ -528,13 +583,14 @@ const defineConfig = async (options, ...extraConfigs) => {
528
583
  });
529
584
  const projectConfigs = await resolveProjectConfigs(
530
585
  configuredProjects,
531
- options?.projectDefaults,
586
+ resolveInheritedProjectDefaults(options),
532
587
  detectRootDir,
533
588
  options?.autoFrameworks
534
589
  );
535
590
  const allConfigs = [...configs, ...projectConfigs.flat(), ...flattenConfigInputs(extraConfigs)];
536
591
  const patchedConfigs = workspacePrefixes?.length ? patchImportGroups(allConfigs, workspacePrefixes) : allConfigs;
537
- return applyStrictMode(patchedConfigs, strict);
592
+ const finalConfig = attachReferencedPlugins(applyStrictMode(patchedConfigs, strict));
593
+ return attachDefineConfigMetadata(finalConfig, { extraConfigs, options });
538
594
  };
539
595
  export {
540
596
  Extension2 as Extension,
@@ -549,6 +605,7 @@ export {
549
605
  Tool,
550
606
  angular,
551
607
  astro,
608
+ attachReferencedPlugins,
552
609
  coreConfig2 as coreConfig,
553
610
  createCoreConfig2 as createCoreConfig,
554
611
  createGitignoreConfig2 as createGitignoreConfig,
@@ -2,6 +2,7 @@ import {
2
2
  createModuleLoader,
3
3
  resolveConfigFeatures
4
4
  } from "@santi020k/eslint-config-core";
5
+ import { isMissingRequestedPackage } from "./optional-package-errors.js";
5
6
  const loadModule = createModuleLoader((specifier) => import.meta.resolve(specifier, import.meta.url));
6
7
  const packCache = /* @__PURE__ */ new Map();
7
8
  const loadFeaturePack = async (specifier) => {
@@ -18,8 +19,9 @@ const loadFeaturePack = async (specifier) => {
18
19
  return module.features;
19
20
  } catch (error) {
20
21
  packCache.delete(specifier);
22
+ const remediation = isMissingRequestedPackage(error, specifier) ? "Install it for the selected options, or remove those options from defineConfig()." : "The installed feature pack failed while evaluating. Inspect this error's cause for the original dependency or runtime failure.";
21
23
  throw new Error(
22
- `Unable to load optional feature pack "${specifier}". Install it for the selected options, or remove those options from defineConfig().`,
24
+ `Unable to load optional feature pack "${specifier}". ` + remediation,
23
25
  { cause: error }
24
26
  );
25
27
  }
@@ -0,0 +1,3 @@
1
+ declare const isMissingRequestedPackage: (error: unknown, specifier: string) => boolean;
2
+
3
+ export { isMissingRequestedPackage };
@@ -0,0 +1,14 @@
1
+ const getPackageRoot = (specifier) => {
2
+ const segments = specifier.split("/");
3
+ return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
4
+ };
5
+ const isMissingRequestedPackage = (error, specifier) => {
6
+ if (!(error instanceof Error)) return false;
7
+ const code = "code" in error ? String(error.code) : "";
8
+ if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") return false;
9
+ const missingSpecifier = /Cannot find (?:module|package) ['"]([^'"]+)['"]/.exec(error.message)?.[1];
10
+ return missingSpecifier !== void 0 && getPackageRoot(missingSpecifier) === getPackageRoot(specifier);
11
+ };
12
+ export {
13
+ isMissingRequestedPackage
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@santi020k/eslint-config-basic",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": true
@@ -30,7 +30,8 @@
30
30
  "files": [
31
31
  "dist",
32
32
  "README.md",
33
- "CHANGELOG.md"
33
+ "CHANGELOG.md",
34
+ "IMPROVEMENTS.md"
34
35
  ],
35
36
  "bin": {
36
37
  "basic-eslint": "./dist/cli.js"
@@ -39,16 +40,16 @@
39
40
  "@typescript-eslint/utils": "^8.65.0",
40
41
  "semver": "^7.8.5",
41
42
  "@santi020k/eslint-config-core": "^3.1.0",
42
- "@santi020k/eslint-config-typescript": "^3.1.0"
43
+ "@santi020k/eslint-config-typescript": "^3.1.1"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@types/node": "^26.1.2",
46
47
  "publint": "^0.3.22",
47
48
  "tsup": "^8.5.1",
48
49
  "typescript": "^6.0.3",
49
- "@santi020k/eslint-config-angular": "^3.1.0",
50
50
  "@santi020k/eslint-config-astro": "^3.1.0",
51
51
  "@santi020k/eslint-config-expo": "^3.1.0",
52
+ "@santi020k/eslint-config-angular": "^3.1.0",
52
53
  "@santi020k/eslint-config-extensions": "^3.1.0",
53
54
  "@santi020k/eslint-config-formats": "^3.1.0",
54
55
  "@santi020k/eslint-config-hono": "^3.1.0",
@@ -73,9 +74,9 @@
73
74
  "peerDependencies": {
74
75
  "eslint": "^10.0.0",
75
76
  "typescript": ">=5.0.0",
76
- "@santi020k/eslint-config-angular": "^3.1.0",
77
77
  "@santi020k/eslint-config-astro": "^3.1.0",
78
78
  "@santi020k/eslint-config-expo": "^3.1.0",
79
+ "@santi020k/eslint-config-angular": "^3.1.0",
79
80
  "@santi020k/eslint-config-extensions": "^3.1.0",
80
81
  "@santi020k/eslint-config-formats": "^3.1.0",
81
82
  "@santi020k/eslint-config-hono": "^3.1.0",
@@ -93,9 +94,9 @@
93
94
  "@santi020k/eslint-config-svelte": "^3.1.0",
94
95
  "@santi020k/eslint-config-tanstack-start": "^3.1.0",
95
96
  "@santi020k/eslint-config-testing": "^3.1.0",
96
- "@santi020k/eslint-config-tools": "^3.1.0",
97
97
  "@santi020k/eslint-config-vite": "^3.1.0",
98
- "@santi020k/eslint-config-vue": "^3.1.0"
98
+ "@santi020k/eslint-config-vue": "^3.1.0",
99
+ "@santi020k/eslint-config-tools": "^3.1.0"
99
100
  },
100
101
  "peerDependenciesMeta": {
101
102
  "@santi020k/eslint-config-angular": {