@utoo/lint 0.4.11 → 0.4.13

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/index.cjs CHANGED
@@ -1313,6 +1313,9 @@ function appendPluginRuleDiagnostics(report, options, { perFileConfig = true } =
1313
1313
  let appended = false;
1314
1314
 
1315
1315
  for (const filePath of filePaths) {
1316
+ if ((report.diagnostics ?? []).some((diagnostic) => diagnostic.filePath === filePath && diagnostic.ruleId === "parse" && diagnostic.severity === "error")) {
1317
+ continue;
1318
+ }
1316
1319
  const matchPath = singleMatchPath ?? filePath;
1317
1320
  const ruleMap = jsPluginRuleMapForFile(options, matchPath, perFileConfig);
1318
1321
  if (ruleMap.size === 0) {
@@ -1500,6 +1503,10 @@ function nativeConfigGlobals(config) {
1500
1503
  return Object.keys(globals).length > 0 ? globals : undefined;
1501
1504
  }
1502
1505
 
1506
+ function nativeConfigSourceType(config) {
1507
+ return config?.languageOptions?.sourceType ?? config?.parserOptions?.sourceType;
1508
+ }
1509
+
1503
1510
  function isGlobalsObject(value) {
1504
1511
  return value !== null && typeof value === "object" && !Array.isArray(value);
1505
1512
  }
@@ -1508,7 +1515,7 @@ function configCanUseNativeRulesFastPath(config) {
1508
1515
  const entries = Array.isArray(config) ? config : [config];
1509
1516
  let hasEnabledRule = false;
1510
1517
  for (const entry of entries) {
1511
- if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry)) {
1518
+ if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry) || nativeConfigSourceType(entry) !== undefined) {
1512
1519
  return false;
1513
1520
  }
1514
1521
  for (const ruleConfig of Object.values(entry.rules ?? {})) {
@@ -1537,8 +1544,12 @@ function nativeSerializableConfig(config) {
1537
1544
  .map((key) => [key, config[key]])
1538
1545
  );
1539
1546
  const globals = nativeConfigGlobals(config);
1540
- if (globals) {
1541
- serialized.languageOptions = { globals };
1547
+ const sourceType = nativeConfigSourceType(config);
1548
+ if (globals || sourceType !== undefined) {
1549
+ serialized.languageOptions = {
1550
+ ...(globals ? { globals } : {}),
1551
+ ...(sourceType === undefined ? {} : { sourceType })
1552
+ };
1542
1553
  }
1543
1554
  return serialized;
1544
1555
  }
@@ -1620,15 +1631,16 @@ function nativeConfigRunsForFiles(paths, options) {
1620
1631
  let rules = calculated.rules;
1621
1632
  const settings = calculated.settings;
1622
1633
  const globals = configured ? nativeConfigGlobals(calculated) : undefined;
1634
+ const sourceType = configured ? nativeConfigSourceType(calculated) : undefined;
1623
1635
  if (configured && options.rules) {
1624
1636
  rules = selectedRulesWithConfigOptions(rules, options.rules);
1625
1637
  }
1626
1638
  if (configured) {
1627
1639
  rules = withoutJsPluginRules(rules, jsPluginRuleIdsForOptions(options, matchPath));
1628
1640
  }
1629
- const signature = configured ? stableConfigSignature({ rules, settings, globals }) : "<native-defaults>";
1641
+ const signature = configured ? stableConfigSignature({ rules, settings, globals, sourceType }) : "<native-defaults>";
1630
1642
  if (!groups.has(signature)) {
1631
- groups.set(signature, { paths: [], rules, settings, globals, configured });
1643
+ groups.set(signature, { paths: [], rules, settings, globals, sourceType, configured });
1632
1644
  }
1633
1645
  groups.get(signature).paths.push(filePath);
1634
1646
  }
@@ -1651,7 +1663,7 @@ function nativeConfigRunsForFiles(paths, options) {
1651
1663
  };
1652
1664
  }
1653
1665
 
1654
- const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals);
1666
+ const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals, group.sourceType);
1655
1667
  if (selectedRules) {
1656
1668
  return {
1657
1669
  paths: group.paths,
@@ -1677,7 +1689,10 @@ function nativeConfigRunsForFiles(paths, options) {
1677
1689
  overrideConfig: {
1678
1690
  rules: allDisabledNativeRules(group.rules),
1679
1691
  ...(group.settings ? { settings: group.settings } : {}),
1680
- ...(group.globals ? { languageOptions: { globals: group.globals } } : {})
1692
+ ...((group.globals || group.sourceType !== undefined) ? { languageOptions: {
1693
+ ...(group.globals ? { globals: group.globals } : {}),
1694
+ ...(group.sourceType === undefined ? {} : { sourceType: group.sourceType })
1695
+ } } : {})
1681
1696
  },
1682
1697
  forceMaterializedConfig: true
1683
1698
  }
@@ -1685,8 +1700,8 @@ function nativeConfigRunsForFiles(paths, options) {
1685
1700
  });
1686
1701
  }
1687
1702
 
1688
- function severityOnlyNativeRuleNames(rules, settings, globals) {
1689
- if (globals || Object.keys(settings ?? {}).length > 0) {
1703
+ function severityOnlyNativeRuleNames(rules, settings, globals, sourceType) {
1704
+ if (globals || sourceType !== undefined || Object.keys(settings ?? {}).length > 0) {
1690
1705
  return undefined;
1691
1706
  }
1692
1707
 
@@ -1774,7 +1789,7 @@ function lintText(code, options = {}) {
1774
1789
 
1775
1790
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-"));
1776
1791
  const requestedPath = textFilePathForOptions(options, "text.js");
1777
- const extension = extname(requestedPath) || ".js";
1792
+ const extension = requestedPath.match(/\.d\.(?:ts|mts|cts)$/)?.[0] ?? (extname(requestedPath) || ".js");
1778
1793
  const tempFile = join(tmp, `input${extension}`);
1779
1794
  const discoveredConfig =
1780
1795
  !options.noConfig && !options.config && !options.overrideConfig
@@ -2350,7 +2365,11 @@ function withTemporaryConfig(options, callback) {
2350
2365
  {}
2351
2366
  );
2352
2367
  const hasGlobals = Object.keys(globals).length > 0;
2353
- if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals) {
2368
+ const sourceType = configs.reduce(
2369
+ (current, config) => nativeConfigSourceType(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd)) ?? current,
2370
+ undefined
2371
+ );
2372
+ if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals && sourceType === undefined) {
2354
2373
  if (shouldMaterializeFileConfig) {
2355
2374
  return callback({
2356
2375
  ...options,
@@ -2362,7 +2381,7 @@ function withTemporaryConfig(options, callback) {
2362
2381
  }
2363
2382
  const enabledRules = enabledRuleNamesFromConfigs(...configs).filter((rule) => !jsPluginRuleIds.has(rule));
2364
2383
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
2365
- if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
2384
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && sourceType === undefined && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
2366
2385
  return callback({
2367
2386
  ...options,
2368
2387
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -2377,7 +2396,10 @@ function withTemporaryConfig(options, callback) {
2377
2396
  writeFileSync(configPath, JSON.stringify({
2378
2397
  rules: nativeRules(rules),
2379
2398
  ...(hasSettings ? { settings } : {}),
2380
- ...(hasGlobals ? { languageOptions: { globals } } : {})
2399
+ ...((hasGlobals || sourceType !== undefined) ? { languageOptions: {
2400
+ ...(hasGlobals ? { globals } : {}),
2401
+ ...(sourceType === undefined ? {} : { sourceType })
2402
+ } } : {})
2381
2403
  }));
2382
2404
  return callback({
2383
2405
  ...options,
@@ -2496,11 +2518,6 @@ class Linter {
2496
2518
  const customRuleMap = customRuleMapForConfig(config, this.rules, normalizedFilePath, verifyOptions.cwd);
2497
2519
  const customRuleFilterMap = customRuleMapForConfig(config, this.rules);
2498
2520
  const customRules = customRuleEntriesForConfig(config, customRuleMap, normalizedFilePath, verifyOptions.cwd);
2499
- const sourceCode = createLinterSourceCode(
2500
- code,
2501
- parserForConfig(config, this.parsers, normalizedFilePath, verifyOptions.cwd),
2502
- { ...parserOptionsForConfig(config, normalizedFilePath, verifyOptions.cwd), requireParser: customRules.length > 0 }
2503
- );
2504
2521
  const nativeConfig = configWithoutCustomRules(config, customRuleFilterMap);
2505
2522
  const lintOptions = {
2506
2523
  cwd: verifyOptions.cwd,
@@ -2512,6 +2529,22 @@ class Linter {
2512
2529
  };
2513
2530
  const report = lintText(code, lintOptions);
2514
2531
  const ruleSeverities = ruleSeverityMapForOptions(lintOptions, normalizedFilePath);
2532
+ const nativeMessages = (report.diagnostics ?? [])
2533
+ .filter((diagnostic) => !isRuleDefinitionError(diagnostic) || !customRuleMap.has(diagnostic.ruleId))
2534
+ .map((diagnostic) => diagnosticToESLintMessage(diagnostic, ruleSeverities));
2535
+ if (nativeMessages.some((message) => message.fatal)) {
2536
+ this.sourceCode = null;
2537
+ this.suppressedMessages = (report.suppressedDiagnostics ?? [])
2538
+ .map((diagnostic) => suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities));
2539
+ this.times = { passes: [] };
2540
+ this.fixPassCount = 0;
2541
+ return sortLintMessages(nativeMessages);
2542
+ }
2543
+ const sourceCode = createLinterSourceCode(
2544
+ code,
2545
+ parserForConfig(config, this.parsers, normalizedFilePath, verifyOptions.cwd),
2546
+ { ...parserOptionsForConfig(config, normalizedFilePath, verifyOptions.cwd), requireParser: customRules.length > 0 }
2547
+ );
2515
2548
  this.sourceCode = sourceCode;
2516
2549
  const customRuleMessages = runCustomLinterRules(customRules, sourceCode, {
2517
2550
  cwd: verifyOptions.cwd,
@@ -2519,7 +2552,7 @@ class Linter {
2519
2552
  config: calculatedConfig({ cwd: verifyOptions.cwd, noConfig: true, overrideConfig: config }, normalizedFilePath)
2520
2553
  });
2521
2554
  const nativeRuleFilter = applyDisableDirectives(
2522
- (report.diagnostics ?? []).map((diagnostic) => diagnosticToESLintMessage(diagnostic, ruleSeverities)),
2555
+ nativeMessages,
2523
2556
  sourceCode
2524
2557
  );
2525
2558
  const customRuleFilter = applyDisableDirectives(customRuleMessages, sourceCode);
@@ -2901,6 +2934,9 @@ function appendCustomLintTextMessages(results, code, filePath, config, rules, op
2901
2934
  return results;
2902
2935
  }
2903
2936
  let result = results.find((item) => item.filePath === filePath);
2937
+ if (result?.fatalErrorCount > 0) {
2938
+ return results;
2939
+ }
2904
2940
  const effectiveCode = typeof result?.output === "string" ? result.output : code;
2905
2941
  const sourceCode = createLinterSourceCode(effectiveCode, null, { ...parserOptionsForConfig(config, filePath, options.cwd), requireParser: true });
2906
2942
  const messages = runCustomLinterRules(customRules, sourceCode, {
@@ -2987,6 +3023,9 @@ function relintAfterCustomFixes(result, code, filePath, customRules, config, rul
2987
3023
 
2988
3024
  function appendCustomLintFileMessages(results, config, options) {
2989
3025
  for (const result of results) {
3026
+ if (result.fatalErrorCount > 0) {
3027
+ continue;
3028
+ }
2990
3029
  const filePath = result.filePath;
2991
3030
  const rules = customRuleMapForConfig(config, new Map(), filePath, options.cwd);
2992
3031
  const customRules = customRuleEntriesForConfig(config, rules, filePath, options.cwd);
@@ -3106,7 +3145,7 @@ function applyDisableDirectives(messages, sourceCode) {
3106
3145
  const kept = [];
3107
3146
  const suppressed = [];
3108
3147
  for (const message of messages) {
3109
- if (!message.ruleId || message.ruleId === "parse" || message.ruleId === "io") {
3148
+ if (!message.ruleId || message.ruleId === "parse" || message.ruleId === "io" || isRuleDefinitionError(message)) {
3110
3149
  kept.push(message);
3111
3150
  continue;
3112
3151
  }
@@ -4520,7 +4559,7 @@ function reportToESLintResults(report, textOptions = {}) {
4520
4559
  byFile.set(filePath, emptyESLintResult(filePath, textOptions.source));
4521
4560
  }
4522
4561
  const ruleSeverities = textOptions.ruleSeverityForFile?.(filePath) ?? textOptions.ruleSeverities;
4523
- if (diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4562
+ if (!isRuleDefinitionError(diagnostic) && diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4524
4563
  continue;
4525
4564
  }
4526
4565
  byFile.get(filePath).messages.push(diagnosticToESLintMessage(diagnostic, ruleSeverities));
@@ -4532,7 +4571,7 @@ function reportToESLintResults(report, textOptions = {}) {
4532
4571
  byFile.set(filePath, emptyESLintResult(filePath, textOptions.source));
4533
4572
  }
4534
4573
  const ruleSeverities = textOptions.ruleSeverityForFile?.(filePath) ?? textOptions.ruleSeverities;
4535
- if (diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4574
+ if (!isRuleDefinitionError(diagnostic) && diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4536
4575
  continue;
4537
4576
  }
4538
4577
  byFile.get(filePath).suppressedMessages.push(suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities));
@@ -4588,8 +4627,19 @@ function applyCompatibilityDisableDirectives(result, textOptions) {
4588
4627
  result.suppressedMessages.push(...filtered.suppressedMessages);
4589
4628
  }
4590
4629
 
4630
+ function isRuleDefinitionError(diagnostic) {
4631
+ return typeof diagnostic?.ruleId === "string"
4632
+ && diagnostic.message === `Definition for rule '${diagnostic.ruleId}' was not found.`;
4633
+ }
4634
+
4591
4635
  function normalizeReportDiagnostics(diagnostics, options = {}) {
4592
4636
  return (diagnostics ?? []).flatMap((diagnostic) => {
4637
+ if (isRuleDefinitionError(diagnostic)) {
4638
+ // Native cannot see JS plugin definitions. A mounted rule is known even
4639
+ // when it is disabled or emits no messages for this file.
4640
+ const filePath = options.pluginTextFilePath ?? diagnostic.filePath;
4641
+ return jsPluginRuleMapForFile(options, filePath).has(diagnostic.ruleId) ? [] : [diagnostic];
4642
+ }
4593
4643
  if (!diagnostic?.ruleId) {
4594
4644
  return [diagnostic];
4595
4645
  }
@@ -5311,12 +5361,18 @@ function emptyESLintResult(filePath, source) {
5311
5361
  function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
5312
5362
  const message = {
5313
5363
  ruleId: diagnostic.ruleId,
5314
- severity: ruleSeverities?.get(diagnostic.ruleId) ?? (diagnostic.severity === "error" ? 2 : 1),
5364
+ severity: isRuleDefinitionError(diagnostic)
5365
+ ? 2
5366
+ : ruleSeverities?.get(diagnostic.ruleId) ?? (diagnostic.severity === "error" ? 2 : 1),
5315
5367
  message: diagnostic.message,
5316
5368
  line: diagnostic.line,
5317
5369
  column: diagnostic.column,
5318
5370
  nodeType: null
5319
5371
  };
5372
+ if (diagnostic.ruleId === "parse" && diagnostic.severity === "error") {
5373
+ message.fatal = true;
5374
+ message.severity = 2;
5375
+ }
5320
5376
  if (typeof diagnostic.endLine === "number") {
5321
5377
  message.endLine = diagnostic.endLine;
5322
5378
  }
@@ -5367,11 +5423,13 @@ function suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities) {
5367
5423
  }
5368
5424
 
5369
5425
  function finalizeESLintResult(result) {
5426
+ result.fatalErrorCount = 0;
5370
5427
  result.errorCount = 0;
5371
5428
  result.warningCount = 0;
5372
5429
  result.fixableErrorCount = 0;
5373
5430
  result.fixableWarningCount = 0;
5374
5431
  for (const message of result.messages) {
5432
+ if (message.fatal) result.fatalErrorCount += 1;
5375
5433
  if (message.severity === 2) {
5376
5434
  result.errorCount += 1;
5377
5435
  if (ruleFixItems(message.fix).length > 0) {
package/index.js CHANGED
@@ -675,11 +675,6 @@ export class Linter {
675
675
  const customRuleMap = customRuleMapForConfig(config, this.rules, normalizedFilePath, verifyOptions.cwd);
676
676
  const customRuleFilterMap = customRuleMapForConfig(config, this.rules);
677
677
  const customRules = customRuleEntriesForConfig(config, customRuleMap, normalizedFilePath, verifyOptions.cwd);
678
- const sourceCode = createLinterSourceCode(
679
- code,
680
- parserForConfig(config, this.parsers, normalizedFilePath, verifyOptions.cwd),
681
- { ...parserOptionsForConfig(config, normalizedFilePath, verifyOptions.cwd), requireParser: customRules.length > 0 }
682
- );
683
678
  const nativeConfig = configWithoutCustomRules(config, customRuleFilterMap);
684
679
  const lintOptions = {
685
680
  cwd: verifyOptions.cwd,
@@ -691,6 +686,22 @@ export class Linter {
691
686
  };
692
687
  const report = lintText(code, lintOptions);
693
688
  const ruleSeverities = ruleSeverityMapForOptions(lintOptions, normalizedFilePath);
689
+ const nativeMessages = (report.diagnostics ?? [])
690
+ .filter((diagnostic) => !isRuleDefinitionError(diagnostic) || !customRuleMap.has(diagnostic.ruleId))
691
+ .map((diagnostic) => diagnosticToESLintMessage(diagnostic, ruleSeverities));
692
+ if (nativeMessages.some((message) => message.fatal)) {
693
+ this.sourceCode = null;
694
+ this.suppressedMessages = (report.suppressedDiagnostics ?? [])
695
+ .map((diagnostic) => suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities));
696
+ this.times = { passes: [] };
697
+ this.fixPassCount = 0;
698
+ return sortLintMessages(nativeMessages);
699
+ }
700
+ const sourceCode = createLinterSourceCode(
701
+ code,
702
+ parserForConfig(config, this.parsers, normalizedFilePath, verifyOptions.cwd),
703
+ { ...parserOptionsForConfig(config, normalizedFilePath, verifyOptions.cwd), requireParser: customRules.length > 0 }
704
+ );
694
705
  this.sourceCode = sourceCode;
695
706
  const customRuleMessages = runCustomLinterRules(customRules, sourceCode, {
696
707
  cwd: verifyOptions.cwd,
@@ -698,7 +709,7 @@ export class Linter {
698
709
  config: calculatedConfig({ cwd: verifyOptions.cwd, noConfig: true, overrideConfig: config }, normalizedFilePath)
699
710
  });
700
711
  const nativeRuleFilter = applyDisableDirectives(
701
- (report.diagnostics ?? []).map((diagnostic) => diagnosticToESLintMessage(diagnostic, ruleSeverities)),
712
+ nativeMessages,
702
713
  sourceCode
703
714
  );
704
715
  const customRuleFilter = applyDisableDirectives(customRuleMessages, sourceCode);
@@ -1080,6 +1091,9 @@ function appendCustomLintTextMessages(results, code, filePath, config, rules, op
1080
1091
  return results;
1081
1092
  }
1082
1093
  let result = results.find((item) => item.filePath === filePath);
1094
+ if (result?.fatalErrorCount > 0) {
1095
+ return results;
1096
+ }
1083
1097
  const effectiveCode = typeof result?.output === "string" ? result.output : code;
1084
1098
  const sourceCode = createLinterSourceCode(effectiveCode, null, { ...parserOptionsForConfig(config, filePath, options.cwd), requireParser: true });
1085
1099
  const messages = runCustomLinterRules(customRules, sourceCode, {
@@ -1166,6 +1180,9 @@ function relintAfterCustomFixes(result, code, filePath, customRules, config, rul
1166
1180
 
1167
1181
  function appendCustomLintFileMessages(results, config, options) {
1168
1182
  for (const result of results) {
1183
+ if (result.fatalErrorCount > 0) {
1184
+ continue;
1185
+ }
1169
1186
  const filePath = result.filePath;
1170
1187
  const rules = customRuleMapForConfig(config, new Map(), filePath, options.cwd);
1171
1188
  const customRules = customRuleEntriesForConfig(config, rules, filePath, options.cwd);
@@ -1285,7 +1302,7 @@ function applyDisableDirectives(messages, sourceCode) {
1285
1302
  const kept = [];
1286
1303
  const suppressed = [];
1287
1304
  for (const message of messages) {
1288
- if (!message.ruleId || message.ruleId === "parse" || message.ruleId === "io") {
1305
+ if (!message.ruleId || message.ruleId === "parse" || message.ruleId === "io" || isRuleDefinitionError(message)) {
1289
1306
  kept.push(message);
1290
1307
  continue;
1291
1308
  }
@@ -3349,6 +3366,9 @@ function appendPluginRuleDiagnostics(report, options, { perFileConfig = true } =
3349
3366
  let appended = false;
3350
3367
 
3351
3368
  for (const filePath of filePaths) {
3369
+ if ((report.diagnostics ?? []).some((diagnostic) => diagnostic.filePath === filePath && diagnostic.ruleId === "parse" && diagnostic.severity === "error")) {
3370
+ continue;
3371
+ }
3352
3372
  const matchPath = singleMatchPath ?? filePath;
3353
3373
  const ruleMap = jsPluginRuleMapForFile(options, matchPath, perFileConfig);
3354
3374
  if (ruleMap.size === 0) {
@@ -3536,6 +3556,10 @@ function nativeConfigGlobals(config) {
3536
3556
  return Object.keys(globals).length > 0 ? globals : undefined;
3537
3557
  }
3538
3558
 
3559
+ function nativeConfigSourceType(config) {
3560
+ return config?.languageOptions?.sourceType ?? config?.parserOptions?.sourceType;
3561
+ }
3562
+
3539
3563
  function isGlobalsObject(value) {
3540
3564
  return value !== null && typeof value === "object" && !Array.isArray(value);
3541
3565
  }
@@ -3544,7 +3568,7 @@ function configCanUseNativeRulesFastPath(config) {
3544
3568
  const entries = Array.isArray(config) ? config : [config];
3545
3569
  let hasEnabledRule = false;
3546
3570
  for (const entry of entries) {
3547
- if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry)) {
3571
+ if (!entry || typeof entry !== "object" || Object.keys(entry.settings ?? {}).length > 0 || nativeConfigGlobals(entry) || nativeConfigSourceType(entry) !== undefined) {
3548
3572
  return false;
3549
3573
  }
3550
3574
  for (const ruleConfig of Object.values(entry.rules ?? {})) {
@@ -3573,8 +3597,12 @@ function nativeSerializableConfig(config) {
3573
3597
  .map((key) => [key, config[key]])
3574
3598
  );
3575
3599
  const globals = nativeConfigGlobals(config);
3576
- if (globals) {
3577
- serialized.languageOptions = { globals };
3600
+ const sourceType = nativeConfigSourceType(config);
3601
+ if (globals || sourceType !== undefined) {
3602
+ serialized.languageOptions = {
3603
+ ...(globals ? { globals } : {}),
3604
+ ...(sourceType === undefined ? {} : { sourceType })
3605
+ };
3578
3606
  }
3579
3607
  return serialized;
3580
3608
  }
@@ -3656,15 +3684,16 @@ function nativeConfigRunsForFiles(paths, options) {
3656
3684
  let rules = calculated.rules;
3657
3685
  const settings = calculated.settings;
3658
3686
  const globals = configured ? nativeConfigGlobals(calculated) : undefined;
3687
+ const sourceType = configured ? nativeConfigSourceType(calculated) : undefined;
3659
3688
  if (configured && options.rules) {
3660
3689
  rules = selectedRulesWithConfigOptions(rules, options.rules);
3661
3690
  }
3662
3691
  if (configured) {
3663
3692
  rules = withoutJsPluginRules(rules, jsPluginRuleIdsForOptions(options, matchPath));
3664
3693
  }
3665
- const signature = configured ? stableConfigSignature({ rules, settings, globals }) : "<native-defaults>";
3694
+ const signature = configured ? stableConfigSignature({ rules, settings, globals, sourceType }) : "<native-defaults>";
3666
3695
  if (!groups.has(signature)) {
3667
- groups.set(signature, { paths: [], rules, settings, globals, configured });
3696
+ groups.set(signature, { paths: [], rules, settings, globals, sourceType, configured });
3668
3697
  }
3669
3698
  groups.get(signature).paths.push(filePath);
3670
3699
  }
@@ -3687,7 +3716,7 @@ function nativeConfigRunsForFiles(paths, options) {
3687
3716
  };
3688
3717
  }
3689
3718
 
3690
- const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals);
3719
+ const selectedRules = severityOnlyNativeRuleNames(group.rules, group.settings, group.globals, group.sourceType);
3691
3720
  if (selectedRules) {
3692
3721
  return {
3693
3722
  paths: group.paths,
@@ -3713,7 +3742,10 @@ function nativeConfigRunsForFiles(paths, options) {
3713
3742
  overrideConfig: {
3714
3743
  rules: allDisabledNativeRules(group.rules),
3715
3744
  ...(group.settings ? { settings: group.settings } : {}),
3716
- ...(group.globals ? { languageOptions: { globals: group.globals } } : {})
3745
+ ...((group.globals || group.sourceType !== undefined) ? { languageOptions: {
3746
+ ...(group.globals ? { globals: group.globals } : {}),
3747
+ ...(group.sourceType === undefined ? {} : { sourceType: group.sourceType })
3748
+ } } : {})
3717
3749
  },
3718
3750
  forceMaterializedConfig: true
3719
3751
  }
@@ -3721,8 +3753,8 @@ function nativeConfigRunsForFiles(paths, options) {
3721
3753
  });
3722
3754
  }
3723
3755
 
3724
- function severityOnlyNativeRuleNames(rules, settings, globals) {
3725
- if (globals || Object.keys(settings ?? {}).length > 0) {
3756
+ function severityOnlyNativeRuleNames(rules, settings, globals, sourceType) {
3757
+ if (globals || sourceType !== undefined || Object.keys(settings ?? {}).length > 0) {
3726
3758
  return undefined;
3727
3759
  }
3728
3760
 
@@ -3810,7 +3842,7 @@ export function lintText(code, options = {}) {
3810
3842
 
3811
3843
  const tmp = mkdtempSync(join(tmpdir(), "utoo-lint-"));
3812
3844
  const requestedPath = textFilePathForOptions(options, "text.js");
3813
- const extension = extname(requestedPath) || ".js";
3845
+ const extension = requestedPath.match(/\.d\.(?:ts|mts|cts)$/)?.[0] ?? (extname(requestedPath) || ".js");
3814
3846
  const tempFile = join(tmp, `input${extension}`);
3815
3847
  const discoveredConfig =
3816
3848
  !options.noConfig && !options.config && !options.overrideConfig
@@ -4386,7 +4418,11 @@ function withTemporaryConfig(options, callback) {
4386
4418
  {}
4387
4419
  );
4388
4420
  const hasGlobals = Object.keys(globals).length > 0;
4389
- if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals) {
4421
+ const sourceType = configs.reduce(
4422
+ (current, config) => nativeConfigSourceType(configDataFromConfig(config, options.filePath ?? options.filename, options.cwd)) ?? current,
4423
+ undefined
4424
+ );
4425
+ if (Object.keys(rules).length === 0 && !hasSettings && !hasGlobals && sourceType === undefined) {
4390
4426
  if (shouldMaterializeFileConfig) {
4391
4427
  return callback({
4392
4428
  ...options,
@@ -4398,7 +4434,7 @@ function withTemporaryConfig(options, callback) {
4398
4434
  }
4399
4435
  const enabledRules = enabledRuleNamesFromConfigs(...configs).filter((rule) => !jsPluginRuleIds.has(rule));
4400
4436
  const hasExplicitOffRules = Object.values(rules).some((value) => ruleConfigSeverity(value) === 0);
4401
- if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
4437
+ if (!shouldMaterializeFileConfig && !hasSettings && !hasGlobals && sourceType === undefined && !hasRuleOptions(rules) && !hasExplicitOffRules && !options.forceMaterializedConfig) {
4402
4438
  return callback({
4403
4439
  ...options,
4404
4440
  config: shouldMaterializeFileConfig ? undefined : options.config,
@@ -4413,7 +4449,10 @@ function withTemporaryConfig(options, callback) {
4413
4449
  writeFileSync(configPath, JSON.stringify({
4414
4450
  rules: nativeRules(rules),
4415
4451
  ...(hasSettings ? { settings } : {}),
4416
- ...(hasGlobals ? { languageOptions: { globals } } : {})
4452
+ ...((hasGlobals || sourceType !== undefined) ? { languageOptions: {
4453
+ ...(hasGlobals ? { globals } : {}),
4454
+ ...(sourceType === undefined ? {} : { sourceType })
4455
+ } } : {})
4417
4456
  }));
4418
4457
  return callback({
4419
4458
  ...options,
@@ -4523,7 +4562,7 @@ function reportToESLintResults(report, textOptions = {}) {
4523
4562
  byFile.set(filePath, emptyESLintResult(filePath, textOptions.source));
4524
4563
  }
4525
4564
  const ruleSeverities = textOptions.ruleSeverityForFile?.(filePath) ?? textOptions.ruleSeverities;
4526
- if (diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4565
+ if (!isRuleDefinitionError(diagnostic) && diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4527
4566
  continue;
4528
4567
  }
4529
4568
  byFile.get(filePath).messages.push(diagnosticToESLintMessage(diagnostic, ruleSeverities));
@@ -4535,7 +4574,7 @@ function reportToESLintResults(report, textOptions = {}) {
4535
4574
  byFile.set(filePath, emptyESLintResult(filePath, textOptions.source));
4536
4575
  }
4537
4576
  const ruleSeverities = textOptions.ruleSeverityForFile?.(filePath) ?? textOptions.ruleSeverities;
4538
- if (diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4577
+ if (!isRuleDefinitionError(diagnostic) && diagnostic.ruleId && ruleSeverities?.get(diagnostic.ruleId) === 0) {
4539
4578
  continue;
4540
4579
  }
4541
4580
  byFile.get(filePath).suppressedMessages.push(suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities));
@@ -4591,8 +4630,19 @@ function applyCompatibilityDisableDirectives(result, textOptions) {
4591
4630
  result.suppressedMessages.push(...filtered.suppressedMessages);
4592
4631
  }
4593
4632
 
4633
+ function isRuleDefinitionError(diagnostic) {
4634
+ return typeof diagnostic?.ruleId === "string"
4635
+ && diagnostic.message === `Definition for rule '${diagnostic.ruleId}' was not found.`;
4636
+ }
4637
+
4594
4638
  function normalizeReportDiagnostics(diagnostics, options = {}) {
4595
4639
  return (diagnostics ?? []).flatMap((diagnostic) => {
4640
+ if (isRuleDefinitionError(diagnostic)) {
4641
+ // Native cannot see JS plugin definitions. A mounted rule is known even
4642
+ // when it is disabled or emits no messages for this file.
4643
+ const filePath = options.pluginTextFilePath ?? diagnostic.filePath;
4644
+ return jsPluginRuleMapForFile(options, filePath).has(diagnostic.ruleId) ? [] : [diagnostic];
4645
+ }
4596
4646
  if (!diagnostic?.ruleId) {
4597
4647
  return [diagnostic];
4598
4648
  }
@@ -5314,12 +5364,18 @@ function emptyESLintResult(filePath, source) {
5314
5364
  function diagnosticToESLintMessage(diagnostic, ruleSeverities) {
5315
5365
  const message = {
5316
5366
  ruleId: diagnostic.ruleId,
5317
- severity: ruleSeverities?.get(diagnostic.ruleId) ?? (diagnostic.severity === "error" ? 2 : 1),
5367
+ severity: isRuleDefinitionError(diagnostic)
5368
+ ? 2
5369
+ : ruleSeverities?.get(diagnostic.ruleId) ?? (diagnostic.severity === "error" ? 2 : 1),
5318
5370
  message: diagnostic.message,
5319
5371
  line: diagnostic.line,
5320
5372
  column: diagnostic.column,
5321
5373
  nodeType: null
5322
5374
  };
5375
+ if (diagnostic.ruleId === "parse" && diagnostic.severity === "error") {
5376
+ message.fatal = true;
5377
+ message.severity = 2;
5378
+ }
5323
5379
  if (typeof diagnostic.endLine === "number") {
5324
5380
  message.endLine = diagnostic.endLine;
5325
5381
  }
@@ -5370,11 +5426,13 @@ function suppressedDiagnosticToESLintMessage(diagnostic, ruleSeverities) {
5370
5426
  }
5371
5427
 
5372
5428
  function finalizeESLintResult(result) {
5429
+ result.fatalErrorCount = 0;
5373
5430
  result.errorCount = 0;
5374
5431
  result.warningCount = 0;
5375
5432
  result.fixableErrorCount = 0;
5376
5433
  result.fixableWarningCount = 0;
5377
5434
  for (const message of result.messages) {
5435
+ if (message.fatal) result.fatalErrorCount += 1;
5378
5436
  if (message.severity === 2) {
5379
5437
  result.errorCount += 1;
5380
5438
  if (ruleFixItems(message.fix).length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.4.11",
3
+ "version": "0.4.13",
4
4
  "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -74,11 +74,11 @@
74
74
  "use-at-your-own-risk.js"
75
75
  ],
76
76
  "optionalDependencies": {
77
- "@utoo/lint-darwin-arm64": "0.4.11",
78
- "@utoo/lint-darwin-x64": "0.4.11",
79
- "@utoo/lint-linux-arm64": "0.4.11",
80
- "@utoo/lint-linux-x64": "0.4.11",
81
- "@utoo/lint-win32-x64": "0.4.11"
77
+ "@utoo/lint-darwin-arm64": "0.4.13",
78
+ "@utoo/lint-darwin-x64": "0.4.13",
79
+ "@utoo/lint-linux-arm64": "0.4.13",
80
+ "@utoo/lint-linux-x64": "0.4.13",
81
+ "@utoo/lint-win32-x64": "0.4.13"
82
82
  },
83
83
  "dependencies": {
84
84
  "@eslint/eslintrc": "2.1.4",
package/schema.json CHANGED
@@ -86,6 +86,11 @@
86
86
  "type": "object",
87
87
  "additionalProperties": true,
88
88
  "properties": {
89
+ "sourceType": {
90
+ "description": "Parse as a module (always strict), script, or CommonJS. Defaults to the source file extension.",
91
+ "type": "string",
92
+ "enum": ["module", "script", "commonjs"]
93
+ },
89
94
  "globals": {
90
95
  "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
96
  "type": "object",