@utoo/lint 0.4.0 → 0.4.1

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/README.md CHANGED
@@ -144,7 +144,8 @@ With utoo, run these scripts as `ut lint` and `ut lint:fix`.
144
144
  | Select rules for one run | `utx @utoo/lint --rules=no-debugger,no-unused-vars src` |
145
145
  | Use an explicit config | `utx @utoo/lint --config=utlint.config.json src` |
146
146
 
147
- Run `utx @utoo/lint --help` for all CLI options.
147
+ Run `utx @utoo/lint --help` for all CLI options. Once the package is installed,
148
+ `utlint` is a shorter alias for the `utoo-lint` command.
148
149
 
149
150
  ## Configuration
150
151
 
@@ -218,8 +219,12 @@ utx @utoo/lint migrate eslint \
218
219
  --output utlint.config.json
219
220
  ```
220
221
 
221
- The package also exposes `eslint` and `fishlint` compatibility commands for
222
- incremental replacement workflows. See the
222
+ The package registers only the `utoo-lint` and `utlint` commands. For
223
+ incremental replacement workflows it also ships an ESLint CLI wrapper at
224
+ `@utoo/lint/bin/eslint.js` and a fishlint compatibility CLI at
225
+ `@utoo/lint/bin/fishlint.js`. Neither is registered as a bin, so installing
226
+ `@utoo/lint` never changes what existing `eslint` or `fishlint` scripts run;
227
+ opt in per script instead. See the
223
228
  [migration guide](https://github.com/utooland/utoo-lint/blob/main/docs/eslint-migration.md)
224
229
  for supported mappings and known differences.
225
230
 
package/bin/eslint.js CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ // ESLint CLI compatibility wrapper. It is deliberately not registered as an
3
+ // `eslint` bin: installing @utoo/lint must never change which tool an
4
+ // existing `eslint` script runs. Scripts opt in explicitly, for example
5
+ // `node node_modules/@utoo/lint/bin/eslint.js src`.
2
6
  import { spawnSync } from "node:child_process";
3
7
  import { fileURLToPath } from "node:url";
4
8
 
File without changes
package/bin/fishlint.js CHANGED
File without changes
package/index.cjs CHANGED
@@ -6,6 +6,7 @@ const { dirname, extname, isAbsolute, join, relative, resolve: resolvePath } = r
6
6
 
7
7
  const { platformPackageName, resolveBinary } = require("./lib/binary.cjs");
8
8
  const { createLegacyConfigResolver } = require("./lib/legacy-config.cjs");
9
+ const { nativeFlatConfigEntries, nativeRules } = require("./lib/native-rule-options.cjs");
9
10
  const { formatESLintResults } = require("./lib/stylish-formatter.cjs");
10
11
  const {
11
12
  findConfigPath: findConfigPathFromDirectory,
@@ -1480,11 +1481,28 @@ function nativeFlatConfigOptions(options) {
1480
1481
  };
1481
1482
  }
1482
1483
 
1484
+ // Globals declared by a config entry, merged the way ESLint does: flat
1485
+ // `languageOptions.globals` wins over eslintrc-style `globals`.
1486
+ function nativeConfigGlobals(config) {
1487
+ if (!config || typeof config !== "object") {
1488
+ return undefined;
1489
+ }
1490
+ const globals = {
1491
+ ...(isGlobalsObject(config.globals) ? config.globals : {}),
1492
+ ...(isGlobalsObject(config.languageOptions?.globals) ? config.languageOptions.globals : {})
1493
+ };
1494
+ return Object.keys(globals).length > 0 ? globals : undefined;
1495
+ }
1496
+
1497
+ function isGlobalsObject(value) {
1498
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1499
+ }
1500
+
1483
1501
  function configCanUseNativeRulesFastPath(config) {
1484
1502
  const entries = Array.isArray(config) ? config : [config];
1485
1503
  let hasEnabledRule = false;
1486
1504
  for (const entry of entries) {
1487
- if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0) {
1505
+ if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry)) {
1488
1506
  return false;
1489
1507
  }
1490
1508
  for (const ruleConfig of Object.values(entry.rules ?? {})) {
@@ -1507,11 +1525,16 @@ function nativeSerializableConfig(config) {
1507
1525
  if (!config || typeof config !== "object") {
1508
1526
  return config;
1509
1527
  }
1510
- return Object.fromEntries(
1528
+ const serialized = Object.fromEntries(
1511
1529
  ["$schema", "name", "files", "ignores", "rules", "settings"]
1512
1530
  .filter((key) => config[key] !== undefined)
1513
1531
  .map((key) => [key, config[key]])
1514
1532
  );
1533
+ const globals = nativeConfigGlobals(config);
1534
+ if (globals) {
1535
+ serialized.languageOptions = { globals };
1536
+ }
1537
+ return serialized;
1515
1538
  }
1516
1539
 
1517
1540
  function expandNativeConfigRunPaths(paths, options) {
@@ -1590,15 +1613,16 @@ function nativeConfigRunsForFiles(paths, options) {
1590
1613
  const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
1591
1614
  let rules = calculated.rules;
1592
1615
  const settings = calculated.settings;
1616
+ const globals = configured ? nativeConfigGlobals(calculated) : undefined;
1593
1617
  if (configured && options.rules) {
1594
1618
  rules = selectedRulesWithConfigOptions(rules, options.rules);
1595
1619
  }
1596
1620
  if (configured) {
1597
1621
  rules = withoutJsPluginRules(rules, jsPluginRuleIdsForOptions(options, matchPath));
1598
1622
  }
1599
- const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
1623
+ const signature = configured ? stableConfigSignature({ rules, settings, globals }) : "<native-defaults>";
1600
1624
  if (!groups.has(signature)) {
1601
- groups.set(signature, { paths: [], rules, settings, configured });
1625
+ groups.set(signature, { paths: [], rules, settings, globals, configured });
1602
1626
  }
1603
1627
  groups.get(signature).paths.push(filePath);
1604
1628
  }
@@ -1621,7 +1645,7 @@ function nativeConfigRunsForFiles(paths, options) {
1621
1645
  };
1622
1646
  }
1623
1647
 
1624
- const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings);
1648
+ const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals);
1625
1649
  if (selectedRules) {
1626
1650
  return {
1627
1651
  paths: group.paths,
@@ -1646,7 +1670,8 @@ function nativeConfigRunsForFiles(paths, options) {
1646
1670
  baseConfig: undefined,
1647
1671
  overrideConfig: {
1648
1672
  rules: allDisabledNativeRules(group.rules),
1649
- ...(group.settings ? { settings: group.settings } : {})
1673
+ ...(group.settings ? { settings: group.settings } : {}),
1674
+ ...(group.globals ? { languageOptions: { globals: group.globals } } : {})
1650
1675
  },
1651
1676
  forceMaterializedConfig: true
1652
1677
  }
@@ -1654,8 +1679,8 @@ function nativeConfigRunsForFiles(paths, options) {
1654
1679
  });
1655
1680
  }
1656
1681
 
1657
- function severityOnlyNativeRuleNames(rules, settings) {
1658
- if (Object.keys(settings ?? {}).length > 0) {
1682
+ function severityOnlyNativeRuleNames(rules, settings, globals) {
1683
+ if (globals || Object.keys(settings ?? {}).length > 0) {
1659
1684
  return undefined;
1660
1685
  }
1661
1686
 
@@ -1979,6 +2004,10 @@ function mergeConfigData(base, override) {
1979
2004
  }
1980
2005
  if (base.languageOptions || override.languageOptions) {
1981
2006
  result.languageOptions = { ...(base.languageOptions ?? {}), ...(override.languageOptions ?? {}) };
2007
+ // ESLint merges `languageOptions.globals` per name across config objects.
2008
+ if (isGlobalsObject(base.languageOptions?.globals) && isGlobalsObject(override.languageOptions?.globals)) {
2009
+ result.languageOptions.globals = { ...base.languageOptions.globals, ...override.languageOptions.globals };
2010
+ }
1982
2011
  }
1983
2012
  if (base.parserOptions || override.parserOptions) {
1984
2013
  result.parserOptions = { ...(base.parserOptions ?? {}), ...(override.parserOptions ?? {}) };
@@ -2274,7 +2303,7 @@ function withTemporaryConfig(options, callback) {
2274
2303
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-flat-config-"));
2275
2304
  const configPath = join(tmp, "utlint.config.json");
2276
2305
  try {
2277
- writeFileSync(configPath, JSON.stringify(options.nativeFlatConfigData));
2306
+ writeFileSync(configPath, JSON.stringify(nativeFlatConfigEntries(options.nativeFlatConfigData)));
2278
2307
  return callback({
2279
2308
  ...options,
2280
2309
  config: configPath,
@@ -2307,7 +2336,15 @@ function withTemporaryConfig(options, callback) {
2307
2336
  {}
2308
2337
  );
2309
2338
  const hasSettings = Object.keys(settings).length > 0;
2310
- if (Object.keys(rules).length === 0 && !hasSettings) {
2339
+ const globals = configs.reduce(
2340
+ (result, config) => ({
2341
+ ...result,
2342
+ ...(nativeConfigGlobals(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd)) ?? {})
2343
+ }),
2344
+ {}
2345
+ );
2346
+ const hasGlobals = Object.keys(globals).length > 0;
2347
+ if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals) {
2311
2348
  if (shouldMaterializeFileConfig) {
2312
2349
  return callback({
2313
2350
  ...options,
@@ -2319,7 +2356,7 @@ function withTemporaryConfig(options, callback) {
2319
2356
  }
2320
2357
  const enabledRules = enabledRuleNamesFromConfigs(...configs).filter((rule) => !jsPluginRuleIds.has(rule));
2321
2358
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
2322
- if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
2359
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
2323
2360
  return callback({
2324
2361
  ...options,
2325
2362
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -2331,7 +2368,11 @@ function withTemporaryConfig(options, callback) {
2331
2368
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
2332
2369
  const configPath = join(tmp, "utlint.config.json");
2333
2370
  try {
2334
- writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
2371
+ writeFileSync(configPath, JSON.stringify({
2372
+ rules: nativeRules(rules),
2373
+ ...(hasSettings ? { settings } : {}),
2374
+ ...(hasGlobals ? { languageOptions: { globals } } : {})
2375
+ }));
2335
2376
  return callback({
2336
2377
  ...options,
2337
2378
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
package/index.js CHANGED
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  import { resolveBinary } from "./lib/binary.js";
9
9
  import { createLegacyConfigResolver } from "./lib/legacy-config.cjs";
10
+ import { nativeFlatConfigEntries, nativeRules } from "./lib/native-rule-options.cjs";
10
11
  import { formatESLintResults } from "./lib/stylish-formatter.cjs";
11
12
  import {
12
13
  findConfigPath as findConfigPathFromDirectory,
@@ -3528,11 +3529,28 @@ function nativeFlatConfigOptions(options) {
3528
3529
  };
3529
3530
  }
3530
3531
 
3532
+ // Globals declared by a config entry, merged the way ESLint does: flat
3533
+ // `languageOptions.globals` wins over eslintrc-style `globals`.
3534
+ function nativeConfigGlobals(config) {
3535
+ if (!config || typeof config !== "object") {
3536
+ return undefined;
3537
+ }
3538
+ const globals = {
3539
+ ...(isGlobalsObject(config.globals) ? config.globals : {}),
3540
+ ...(isGlobalsObject(config.languageOptions?.globals) ? config.languageOptions.globals : {})
3541
+ };
3542
+ return Object.keys(globals).length > 0 ? globals : undefined;
3543
+ }
3544
+
3545
+ function isGlobalsObject(value) {
3546
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3547
+ }
3548
+
3531
3549
  function configCanUseNativeRulesFastPath(config) {
3532
3550
  const entries = Array.isArray(config) ? config : [config];
3533
3551
  let hasEnabledRule = false;
3534
3552
  for (const entry of entries) {
3535
- if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0) {
3553
+ if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry)) {
3536
3554
  return false;
3537
3555
  }
3538
3556
  for (const ruleConfig of Object.values(entry.rules ?? {})) {
@@ -3555,11 +3573,16 @@ function nativeSerializableConfig(config) {
3555
3573
  if (!config || typeof config !== "object") {
3556
3574
  return config;
3557
3575
  }
3558
- return Object.fromEntries(
3576
+ const serialized = Object.fromEntries(
3559
3577
  ["$schema", "name", "files", "ignores", "rules", "settings"]
3560
3578
  .filter((key) => config[key] !== undefined)
3561
3579
  .map((key) => [key, config[key]])
3562
3580
  );
3581
+ const globals = nativeConfigGlobals(config);
3582
+ if (globals) {
3583
+ serialized.languageOptions = { globals };
3584
+ }
3585
+ return serialized;
3563
3586
  }
3564
3587
 
3565
3588
  function expandNativeConfigRunPaths(paths, options) {
@@ -3638,15 +3661,16 @@ function nativeConfigRunsForFiles(paths, options) {
3638
3661
  const calculated = configured ? calculatedConfig({ ...options, rules: undefined }, matchPath) : {};
3639
3662
  let rules = calculated.rules;
3640
3663
  const settings = calculated.settings;
3664
+ const globals = configured ? nativeConfigGlobals(calculated) : undefined;
3641
3665
  if (configured && options.rules) {
3642
3666
  rules = selectedRulesWithConfigOptions(rules, options.rules);
3643
3667
  }
3644
3668
  if (configured) {
3645
3669
  rules = withoutJsPluginRules(rules, jsPluginRuleIdsForOptions(options, matchPath));
3646
3670
  }
3647
- const signature = configured ? stableConfigSignature({ rules, settings }) : "<native-defaults>";
3671
+ const signature = configured ? stableConfigSignature({ rules, settings, globals }) : "<native-defaults>";
3648
3672
  if (!groups.has(signature)) {
3649
- groups.set(signature, { paths: [], rules, settings, configured });
3673
+ groups.set(signature, { paths: [], rules, settings, globals, configured });
3650
3674
  }
3651
3675
  groups.get(signature).paths.push(filePath);
3652
3676
  }
@@ -3669,7 +3693,7 @@ function nativeConfigRunsForFiles(paths, options) {
3669
3693
  };
3670
3694
  }
3671
3695
 
3672
- const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings);
3696
+ const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals);
3673
3697
  if (selectedRules) {
3674
3698
  return {
3675
3699
  paths: group.paths,
@@ -3694,7 +3718,8 @@ function nativeConfigRunsForFiles(paths, options) {
3694
3718
  baseConfig: undefined,
3695
3719
  overrideConfig: {
3696
3720
  rules: allDisabledNativeRules(group.rules),
3697
- ...(group.settings ? { settings: group.settings } : {})
3721
+ ...(group.settings ? { settings: group.settings } : {}),
3722
+ ...(group.globals ? { languageOptions: { globals: group.globals } } : {})
3698
3723
  },
3699
3724
  forceMaterializedConfig: true
3700
3725
  }
@@ -3702,8 +3727,8 @@ function nativeConfigRunsForFiles(paths, options) {
3702
3727
  });
3703
3728
  }
3704
3729
 
3705
- function severityOnlyNativeRuleNames(rules, settings) {
3706
- if (Object.keys(settings ?? {}).length > 0) {
3730
+ function severityOnlyNativeRuleNames(rules, settings, globals) {
3731
+ if (globals || Object.keys(settings ?? {}).length > 0) {
3707
3732
  return undefined;
3708
3733
  }
3709
3734
 
@@ -4027,6 +4052,10 @@ function mergeConfigData(base, override) {
4027
4052
  }
4028
4053
  if (base.languageOptions || override.languageOptions) {
4029
4054
  result.languageOptions = { ...(base.languageOptions ?? {}), ...(override.languageOptions ?? {}) };
4055
+ // ESLint merges `languageOptions.globals` per name across config objects.
4056
+ if (isGlobalsObject(base.languageOptions?.globals) && isGlobalsObject(override.languageOptions?.globals)) {
4057
+ result.languageOptions.globals = { ...base.languageOptions.globals, ...override.languageOptions.globals };
4058
+ }
4030
4059
  }
4031
4060
  if (base.parserOptions || override.parserOptions) {
4032
4061
  result.parserOptions = { ...(base.parserOptions ?? {}), ...(override.parserOptions ?? {}) };
@@ -4322,7 +4351,7 @@ function withTemporaryConfig(options, callback) {
4322
4351
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-flat-config-"));
4323
4352
  const configPath = join(tmp, "utlint.config.json");
4324
4353
  try {
4325
- writeFileSync(configPath, JSON.stringify(options.nativeFlatConfigData));
4354
+ writeFileSync(configPath, JSON.stringify(nativeFlatConfigEntries(options.nativeFlatConfigData)));
4326
4355
  return callback({
4327
4356
  ...options,
4328
4357
  config: configPath,
@@ -4355,7 +4384,15 @@ function withTemporaryConfig(options, callback) {
4355
4384
  {}
4356
4385
  );
4357
4386
  const hasSettings = Object.keys(settings).length > 0;
4358
- if (Object.keys(rules).length === 0 && !hasSettings) {
4387
+ const globals = configs.reduce(
4388
+ (result, config) => ({
4389
+ ...result,
4390
+ ...(nativeConfigGlobals(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd)) ?? {})
4391
+ }),
4392
+ {}
4393
+ );
4394
+ const hasGlobals = Object.keys(globals).length > 0;
4395
+ if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals) {
4359
4396
  if (shouldMaterializeFileConfig) {
4360
4397
  return callback({
4361
4398
  ...options,
@@ -4367,7 +4404,7 @@ function withTemporaryConfig(options, callback) {
4367
4404
  }
4368
4405
  const enabledRules = enabledRuleNamesFromConfigs(...configs).filter((rule) => !jsPluginRuleIds.has(rule));
4369
4406
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
4370
- if (!shouldMaterializeFileConfig && !hasSettings && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
4407
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
4371
4408
  return callback({
4372
4409
  ...options,
4373
4410
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -4379,7 +4416,11 @@ function withTemporaryConfig(options, callback) {
4379
4416
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-config-"));
4380
4417
  const configPath = join(tmp, "utlint.config.json");
4381
4418
  try {
4382
- writeFileSync(configPath, JSON.stringify({ rules, ...(hasSettings ? { settings } : {}) }));
4419
+ writeFileSync(configPath, JSON.stringify({
4420
+ rules: nativeRules(rules),
4421
+ ...(hasSettings ? { settings } : {}),
4422
+ ...(hasGlobals ? { languageOptions: { globals } } : {})
4423
+ }));
4383
4424
  return callback({
4384
4425
  ...options,
4385
4426
  config: shouldMaterializeFileConfig ? configPath : options.config ?? configPath,
@@ -2,6 +2,8 @@ import { dirname } from "node:path";
2
2
  import { writeFileSync } from "node:fs";
3
3
  import { createJiti } from "jiti";
4
4
 
5
+ import { nativeRules } from "./native-rule-options.cjs";
6
+
5
7
  const configPath = process.argv[2];
6
8
 
7
9
  // Config modules may mount live ESLint plugin objects, which contain functions
@@ -59,7 +61,12 @@ function serializable(value, seen = new WeakSet()) {
59
61
  result.plugins = serializablePlugins(item, seen);
60
62
  continue;
61
63
  }
62
- const serialized = serializable(item, seen);
64
+ // Rule options such as `maxDepth: Infinity` have a JSON spelling the
65
+ // native binary understands; rewrite them before Infinity becomes null.
66
+ // Other non-finite values stay null here because this loader cannot
67
+ // tell native rules from plugin rules; the wrapper reports the native
68
+ // ones with the rule and option name.
69
+ const serialized = serializable(key === "rules" ? nativeRules(item, { strict: false }) : item, seen);
63
70
  if (serialized !== undefined) {
64
71
  result[key] = serialized;
65
72
  }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+
3
+ // JSON has no representation for Infinity or NaN, so JSON.stringify turns them
4
+ // into null and the native binary rejects the rule option with an opaque
5
+ // UnsupportedRuleConfigValue error. Options with a documented unlimited
6
+ // spelling are rewritten to it. Anything else fails with a message that names
7
+ // the rule and option, or becomes null again in lenient mode (used by the
8
+ // config loader subprocess, which cannot tell native rules from plugin rules).
9
+ const UNLIMITED = "∞";
10
+ const INFINITE_OPTION_SPELLINGS = {
11
+ "import/no-cycle": { maxDepth: UNLIMITED }
12
+ };
13
+
14
+ function nativeRuleConfig(ruleId, ruleConfig, options = {}) {
15
+ if (!Array.isArray(ruleConfig)) {
16
+ return ruleConfig;
17
+ }
18
+ const strict = options.strict !== false;
19
+ const spellings = INFINITE_OPTION_SPELLINGS[ruleId] ?? {};
20
+ return ruleConfig.map((item, index) => {
21
+ if (index === 0) {
22
+ return item;
23
+ }
24
+ if (!isPlainObject(item)) {
25
+ return nativeRuleOptionValue(ruleId, item, `[${index}]`, strict);
26
+ }
27
+ const seen = new WeakSet([item]);
28
+ return Object.fromEntries(
29
+ Object.entries(item).map(([key, value]) => [
30
+ key,
31
+ value === Infinity && Object.hasOwn(spellings, key)
32
+ ? spellings[key]
33
+ : nativeRuleOptionValue(ruleId, value, key, strict, seen)
34
+ ])
35
+ );
36
+ });
37
+ }
38
+
39
+ function nativeRuleOptionValue(ruleId, value, path, strict, seen = new WeakSet()) {
40
+ if (typeof value === "number") {
41
+ if (Number.isFinite(value)) {
42
+ return value;
43
+ }
44
+ if (!strict) {
45
+ return null;
46
+ }
47
+ throw new TypeError(
48
+ `Rule "${ruleId}" option "${path}" is ${String(value)}, which cannot be written to the native JSON config.`
49
+ );
50
+ }
51
+ if (value === null || typeof value !== "object" || seen.has(value)) {
52
+ return value;
53
+ }
54
+ seen.add(value);
55
+ try {
56
+ if (Array.isArray(value)) {
57
+ return value.map((item, index) => nativeRuleOptionValue(ruleId, item, `${path}[${index}]`, strict, seen));
58
+ }
59
+ if (!isPlainObject(value)) {
60
+ return value;
61
+ }
62
+ return Object.fromEntries(
63
+ Object.entries(value).map(([key, item]) => [
64
+ key,
65
+ nativeRuleOptionValue(ruleId, item, `${path}.${key}`, strict, seen)
66
+ ])
67
+ );
68
+ } finally {
69
+ seen.delete(value);
70
+ }
71
+ }
72
+
73
+ function nativeRules(rules, options = {}) {
74
+ if (!isPlainObject(rules)) {
75
+ return rules;
76
+ }
77
+ return Object.fromEntries(
78
+ Object.entries(rules).map(([ruleId, ruleConfig]) => [ruleId, nativeRuleConfig(ruleId, ruleConfig, options)])
79
+ );
80
+ }
81
+
82
+ function nativeFlatConfigEntries(entries, options = {}) {
83
+ if (!Array.isArray(entries)) {
84
+ return entries;
85
+ }
86
+ return entries.map((entry) =>
87
+ isPlainObject(entry) && entry.rules ? { ...entry, rules: nativeRules(entry.rules, options) } : entry
88
+ );
89
+ }
90
+
91
+ function isPlainObject(value) {
92
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
93
+ return false;
94
+ }
95
+ const prototype = Object.getPrototypeOf(value);
96
+ return prototype === Object.prototype || prototype === null;
97
+ }
98
+
99
+ module.exports = { nativeFlatConfigEntries, nativeRuleConfig, nativeRules };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -50,9 +50,7 @@
50
50
  "./package.json": "./package.json"
51
51
  },
52
52
  "bin": {
53
- "eslint": "./bin/eslint.js",
54
- "fishlint": "./bin/fishlint.js",
55
- "fishlint-lint-staged": "./bin/fishlint-lint-staged.js",
53
+ "utlint": "./bin/utoo-lint.js",
56
54
  "utoo-lint": "./bin/utoo-lint.js"
57
55
  },
58
56
  "files": [
@@ -76,11 +74,11 @@
76
74
  "use-at-your-own-risk.js"
77
75
  ],
78
76
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.4.0",
80
- "@utoo/lint-darwin-x64": "0.4.0",
81
- "@utoo/lint-linux-arm64": "0.4.0",
82
- "@utoo/lint-linux-x64": "0.4.0",
83
- "@utoo/lint-win32-x64": "0.4.0"
77
+ "@utoo/lint-darwin-arm64": "0.4.1",
78
+ "@utoo/lint-darwin-x64": "0.4.1",
79
+ "@utoo/lint-linux-arm64": "0.4.1",
80
+ "@utoo/lint-linux-x64": "0.4.1",
81
+ "@utoo/lint-win32-x64": "0.4.1"
84
82
  },
85
83
  "dependencies": {
86
84
  "@eslint/eslintrc": "2.1.4",
package/schema.json CHANGED
@@ -82,6 +82,30 @@
82
82
  }
83
83
  ]
84
84
  },
85
+ "languageOptions": {
86
+ "type": "object",
87
+ "additionalProperties": true,
88
+ "properties": {
89
+ "globals": {
90
+ "description": "Globals declared for matching files, using ESLint flat config values. \"readonly\" accepts reads, \"writable\" also accepts writes, and \"off\" removes a built-in global.",
91
+ "type": "object",
92
+ "additionalProperties": {
93
+ "oneOf": [
94
+ {
95
+ "type": "string",
96
+ "enum": ["readonly", "readable", "writable", "writeable", "off"]
97
+ },
98
+ {
99
+ "type": "boolean"
100
+ },
101
+ {
102
+ "type": "null"
103
+ }
104
+ ]
105
+ }
106
+ }
107
+ }
108
+ },
85
109
  "ignores": {
86
110
  "description": "File globs ignored by this config entry.",
87
111
  "oneOf": [