@utoo/lint 0.4.0 → 0.4.2
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 +8 -3
- package/bin/eslint.js +4 -0
- package/bin/fishlint-lint-staged.js +0 -0
- package/bin/fishlint.js +0 -0
- package/index.cjs +53 -12
- package/index.js +53 -12
- package/lib/load-config.js +8 -1
- package/lib/native-rule-options.cjs +99 -0
- package/package.json +7 -9
- package/schema.json +24 -0
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
|
|
222
|
-
incremental replacement workflows
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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,
|
package/lib/load-config.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
3
|
+
"version": "0.4.2",
|
|
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
|
-
"
|
|
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.
|
|
80
|
-
"@utoo/lint-darwin-x64": "0.4.
|
|
81
|
-
"@utoo/lint-linux-arm64": "0.4.
|
|
82
|
-
"@utoo/lint-linux-x64": "0.4.
|
|
83
|
-
"@utoo/lint-win32-x64": "0.4.
|
|
77
|
+
"@utoo/lint-darwin-arm64": "0.4.2",
|
|
78
|
+
"@utoo/lint-darwin-x64": "0.4.2",
|
|
79
|
+
"@utoo/lint-linux-arm64": "0.4.2",
|
|
80
|
+
"@utoo/lint-linux-x64": "0.4.2",
|
|
81
|
+
"@utoo/lint-win32-x64": "0.4.2"
|
|
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": [
|