@utoo/lint 0.4.9 → 0.4.11

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
@@ -16,6 +16,7 @@ const {
16
16
  const configModule = require("./lib/config-module.cjs");
17
17
  const estreeParser = require("./lib/estree-parser.cjs");
18
18
  const pluginRunner = require("./lib/plugin-runner.cjs");
19
+ const { tokenWindow, selectTokenItems } = require("./lib/source-code-tokens.cjs");
19
20
 
20
21
  const version = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8")).version;
21
22
 
@@ -1438,6 +1439,11 @@ function appendPluginRuleDiagnostics(report, options, { perFileConfig = true } =
1438
1439
  }
1439
1440
 
1440
1441
  function nativeFlatConfigOptions(options) {
1442
+ // lintText stores source in a temporary file. Resolve its configuration
1443
+ // against the logical filename before passing the materialized config to native.
1444
+ if (options.pluginTextFilePath !== undefined) {
1445
+ return undefined;
1446
+ }
1441
1447
  if (defersLegacyIgnores(options)) {
1442
1448
  // A legacy config may cascade from a nested directory even with baseConfig.
1443
1449
  return undefined;
@@ -1605,7 +1611,7 @@ function nativeConfigRunsForFiles(paths, options) {
1605
1611
  let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
1606
1612
  for (const filePath of paths) {
1607
1613
  const matchPath = paths.length === 1
1608
- ? options.filePath ?? options.filename ?? filePath
1614
+ ? options.filePath ?? options.filename ?? options.pluginTextFilePath ?? filePath
1609
1615
  : filePath;
1610
1616
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
1611
1617
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath || legacyConfigForFile(options, matchPath));
@@ -3519,8 +3525,14 @@ class SourceCode {
3519
3525
  getLocFromIndex(index) {
3520
3526
  const clamped = Math.max(0, Math.min(index, this.text.length));
3521
3527
  let line = 0;
3522
- while (line + 1 < this.lineStartIndices.length && this.lineStartIndices[line + 1] <= clamped) {
3523
- line += 1;
3528
+ let end = this.lineStartIndices.length - 1;
3529
+ while (line < end) {
3530
+ const middle = line + Math.ceil((end - line) / 2);
3531
+ if (this.lineStartIndices[middle] <= clamped) {
3532
+ line = middle;
3533
+ } else {
3534
+ end = middle - 1;
3535
+ }
3524
3536
  }
3525
3537
  return {
3526
3538
  line: line + 1,
@@ -3564,64 +3576,64 @@ class SourceCode {
3564
3576
  }
3565
3577
 
3566
3578
  getFirstToken(node, skipOrOptions = 0) {
3567
- return sourceForwardToken(this.getTokens(node, sourceTokenOptions(skipOrOptions, "skip")), skipOrOptions);
3579
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), false, true);
3568
3580
  }
3569
3581
 
3570
3582
  getFirstTokens(node, countOrOptions = 1) {
3571
- return sourceForwardTokens(this.getTokens(node, sourceTokenOptions(countOrOptions, "count")), countOrOptions);
3583
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), false, false);
3572
3584
  }
3573
3585
 
3574
3586
  getLastToken(node, skipOrOptions = 0) {
3575
- return sourceBackwardToken(this.getTokens(node, sourceTokenOptions(skipOrOptions, "skip")), skipOrOptions);
3587
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), true, true);
3576
3588
  }
3577
3589
 
3578
3590
  getLastTokens(node, countOrOptions = 1) {
3579
- return sourceBackwardTokens(this.getTokens(node, sourceTokenOptions(countOrOptions, "count")), countOrOptions);
3591
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), true, false);
3580
3592
  }
3581
3593
 
3582
3594
  getTokenBefore(nodeOrToken, skipOrOptions = 0) {
3583
3595
  const options = sourceTokenOptions(skipOrOptions, "skip");
3584
- return sourceBackwardToken(sourceApplyTokenFilter(sourceItemsBefore(sourceTokenItems(this, options), this.getRange(nodeOrToken)[0]), options), options);
3596
+ return tokenWindow(this, options, null, this.getRange(nodeOrToken)[0], true, true);
3585
3597
  }
3586
3598
 
3587
3599
  getTokensBefore(nodeOrToken, countOrOptions = 1) {
3588
- const options = sourceTokenOptions(countOrOptions, "count");
3589
- return sourceBackwardTokens(sourceApplyTokenFilter(sourceItemsBefore(sourceTokenItems(this, options), this.getRange(nodeOrToken)[0]), options), options);
3600
+ const options = sourceCountedTokenOptions(countOrOptions);
3601
+ return tokenWindow(this, options, null, this.getRange(nodeOrToken)[0], true, false);
3590
3602
  }
3591
3603
 
3592
3604
  getTokenAfter(nodeOrToken, skipOrOptions = 0) {
3593
3605
  const options = sourceTokenOptions(skipOrOptions, "skip");
3594
- return sourceForwardToken(sourceApplyTokenFilter(sourceItemsAfter(sourceTokenItems(this, options), this.getRange(nodeOrToken)[1]), options), options);
3606
+ return tokenWindow(this, options, this.getRange(nodeOrToken)[1], null, false, true);
3595
3607
  }
3596
3608
 
3597
3609
  getTokensAfter(nodeOrToken, countOrOptions = 1) {
3598
- const options = sourceTokenOptions(countOrOptions, "count");
3599
- return sourceForwardTokens(sourceApplyTokenFilter(sourceItemsAfter(sourceTokenItems(this, options), this.getRange(nodeOrToken)[1]), options), options);
3610
+ const options = sourceCountedTokenOptions(countOrOptions);
3611
+ return tokenWindow(this, options, this.getRange(nodeOrToken)[1], null, false, false);
3600
3612
  }
3601
3613
 
3602
3614
  getTokensBetween(left, right, optionsOrCount) {
3603
- const options = sourceTokenOptions(optionsOrCount, "count");
3604
- return sourceForwardTokens(sourceTokensBetween(this, left, right, options), options);
3615
+ const options = sourceCountedTokenOptions(optionsOrCount);
3616
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, false);
3605
3617
  }
3606
3618
 
3607
3619
  getFirstTokenBetween(left, right, skipOrOptions = 0) {
3608
3620
  const options = sourceTokenOptions(skipOrOptions, "skip");
3609
- return sourceForwardToken(sourceTokensBetween(this, left, right, options), options);
3621
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, true);
3610
3622
  }
3611
3623
 
3612
3624
  getFirstTokensBetween(left, right, countOrOptions = 1) {
3613
- const options = sourceTokenOptions(countOrOptions, "count");
3614
- return sourceForwardTokens(sourceTokensBetween(this, left, right, options), options);
3625
+ const options = sourceCountedTokenOptions(countOrOptions);
3626
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, false);
3615
3627
  }
3616
3628
 
3617
3629
  getLastTokenBetween(left, right, skipOrOptions = 0) {
3618
3630
  const options = sourceTokenOptions(skipOrOptions, "skip");
3619
- return sourceBackwardToken(sourceTokensBetween(this, left, right, options), options);
3631
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], true, true);
3620
3632
  }
3621
3633
 
3622
3634
  getLastTokensBetween(left, right, countOrOptions = 1) {
3623
- const options = sourceTokenOptions(countOrOptions, "count");
3624
- return sourceBackwardTokens(sourceTokensBetween(this, left, right, options), options);
3635
+ const options = sourceCountedTokenOptions(countOrOptions);
3636
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], true, false);
3625
3637
  }
3626
3638
 
3627
3639
  getTokenByRangeStart(index, options = {}) {
@@ -3629,11 +3641,11 @@ class SourceCode {
3629
3641
  }
3630
3642
 
3631
3643
  getTokenOrCommentBefore(nodeOrToken) {
3632
- return sourceItemsBefore(sourceTokensAndComments(this), this.getRange(nodeOrToken)[0]).at(-1) ?? null;
3644
+ return this.getTokenBefore(nodeOrToken, { includeComments: true });
3633
3645
  }
3634
3646
 
3635
3647
  getTokenOrCommentAfter(nodeOrToken) {
3636
- return sourceItemsAfter(sourceTokensAndComments(this), this.getRange(nodeOrToken)[1])[0] ?? null;
3648
+ return this.getTokenAfter(nodeOrToken, { includeComments: true });
3637
3649
  }
3638
3650
 
3639
3651
  getCommentsBefore(nodeOrToken) {
@@ -3806,11 +3818,12 @@ function sourceTokensAndComments(sourceCode) {
3806
3818
  return [...sourceCode.tokens, ...sourceCode.comments].sort((left, right) => (left.range?.[0] ?? 0) - (right.range?.[0] ?? 0));
3807
3819
  }
3808
3820
 
3809
- function sourceTokensBetween(sourceCode, left, right, options) {
3810
- return sourceApplyTokenFilter(
3811
- sourceItemsBetween(sourceTokenItems(sourceCode, options), sourceCode.getRange(left)[1], sourceCode.getRange(right)[0]),
3812
- options
3813
- );
3821
+ function sourceNodeTokenWindow(sourceCode, node, options, backwards, single) {
3822
+ if (!node) {
3823
+ return selectTokenItems(sourceTokenItems(sourceCode, options), options, backwards, single);
3824
+ }
3825
+ const range = expandSourceRange(sourceCode.getRange(node), options.beforeCount, options.afterCount, sourceCode.text.length);
3826
+ return tokenWindow(sourceCode, options, range[0], range[1], backwards, single);
3814
3827
  }
3815
3828
 
3816
3829
  function sourceTokenItems(sourceCode, options) {
@@ -3843,6 +3856,14 @@ function sourceTokenOptions(optionsOrNumber, numericKey) {
3843
3856
  };
3844
3857
  }
3845
3858
 
3859
+ // Adjacent/between token APIs default an omitted count to one, including
3860
+ // options objects; getFirstTokens/getLastTokens keep their unbounded form.
3861
+ function sourceCountedTokenOptions(value) {
3862
+ const options = sourceTokenOptions(value, "count");
3863
+ options.count ??= 1;
3864
+ return options;
3865
+ }
3866
+
3846
3867
  function sourceTokenCount(value, defaultValue) {
3847
3868
  return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : defaultValue;
3848
3869
  }
@@ -3851,33 +3872,6 @@ function sourceApplyTokenFilter(items, options) {
3851
3872
  return options.filter ? items.filter((item) => options.filter(item)) : items;
3852
3873
  }
3853
3874
 
3854
- function sourceForwardToken(items, optionsOrNumber) {
3855
- const options = sourceTokenOptions(optionsOrNumber, "skip");
3856
- return items[options.skip] ?? null;
3857
- }
3858
-
3859
- function sourceForwardTokens(items, optionsOrNumber) {
3860
- const options = sourceTokenOptions(optionsOrNumber, "count");
3861
- const start = options.skip;
3862
- const end = options.count === null ? undefined : start + options.count;
3863
- return items.slice(start, end);
3864
- }
3865
-
3866
- function sourceBackwardToken(items, optionsOrNumber) {
3867
- const options = sourceTokenOptions(optionsOrNumber, "skip");
3868
- return items.at(-(options.skip + 1)) ?? null;
3869
- }
3870
-
3871
- function sourceBackwardTokens(items, optionsOrNumber) {
3872
- const options = sourceTokenOptions(optionsOrNumber, "count");
3873
- if (options.count === 0) {
3874
- return [];
3875
- }
3876
- const end = options.skip === 0 ? undefined : -options.skip;
3877
- const available = end === undefined ? items : items.slice(0, end);
3878
- return options.count === null ? available : available.slice(-options.count);
3879
- }
3880
-
3881
3875
  function sourceNodeByRangeIndex(node, index, seen = new Set()) {
3882
3876
  if (!node || typeof node !== "object" || seen.has(node)) {
3883
3877
  return null;
package/index.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  import configModule from "./lib/config-module.cjs";
18
18
  import estreeParser from "./lib/estree-parser.cjs";
19
19
  import pluginRunner from "./lib/plugin-runner.cjs";
20
+ import { tokenWindow, selectTokenItems } from "./lib/source-code-tokens.cjs";
20
21
 
21
22
  export { platformPackageName, resolveBinary } from "./lib/binary.js";
22
23
 
@@ -1703,8 +1704,14 @@ export class SourceCode {
1703
1704
  getLocFromIndex(index) {
1704
1705
  const clamped = Math.max(0, Math.min(index, this.text.length));
1705
1706
  let line = 0;
1706
- while (line + 1 < this.lineStartIndices.length && this.lineStartIndices[line + 1] <= clamped) {
1707
- line += 1;
1707
+ let end = this.lineStartIndices.length - 1;
1708
+ while (line < end) {
1709
+ const middle = line + Math.ceil((end - line) / 2);
1710
+ if (this.lineStartIndices[middle] <= clamped) {
1711
+ line = middle;
1712
+ } else {
1713
+ end = middle - 1;
1714
+ }
1708
1715
  }
1709
1716
  return {
1710
1717
  line: line + 1,
@@ -1748,64 +1755,64 @@ export class SourceCode {
1748
1755
  }
1749
1756
 
1750
1757
  getFirstToken(node, skipOrOptions = 0) {
1751
- return sourceForwardToken(this.getTokens(node, sourceTokenOptions(skipOrOptions, "skip")), skipOrOptions);
1758
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), false, true);
1752
1759
  }
1753
1760
 
1754
1761
  getFirstTokens(node, countOrOptions = 1) {
1755
- return sourceForwardTokens(this.getTokens(node, sourceTokenOptions(countOrOptions, "count")), countOrOptions);
1762
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), false, false);
1756
1763
  }
1757
1764
 
1758
1765
  getLastToken(node, skipOrOptions = 0) {
1759
- return sourceBackwardToken(this.getTokens(node, sourceTokenOptions(skipOrOptions, "skip")), skipOrOptions);
1766
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), true, true);
1760
1767
  }
1761
1768
 
1762
1769
  getLastTokens(node, countOrOptions = 1) {
1763
- return sourceBackwardTokens(this.getTokens(node, sourceTokenOptions(countOrOptions, "count")), countOrOptions);
1770
+ return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), true, false);
1764
1771
  }
1765
1772
 
1766
1773
  getTokenBefore(nodeOrToken, skipOrOptions = 0) {
1767
1774
  const options = sourceTokenOptions(skipOrOptions, "skip");
1768
- return sourceBackwardToken(sourceApplyTokenFilter(sourceItemsBefore(sourceTokenItems(this, options), this.getRange(nodeOrToken)[0]), options), options);
1775
+ return tokenWindow(this, options, null, this.getRange(nodeOrToken)[0], true, true);
1769
1776
  }
1770
1777
 
1771
1778
  getTokensBefore(nodeOrToken, countOrOptions = 1) {
1772
- const options = sourceTokenOptions(countOrOptions, "count");
1773
- return sourceBackwardTokens(sourceApplyTokenFilter(sourceItemsBefore(sourceTokenItems(this, options), this.getRange(nodeOrToken)[0]), options), options);
1779
+ const options = sourceCountedTokenOptions(countOrOptions);
1780
+ return tokenWindow(this, options, null, this.getRange(nodeOrToken)[0], true, false);
1774
1781
  }
1775
1782
 
1776
1783
  getTokenAfter(nodeOrToken, skipOrOptions = 0) {
1777
1784
  const options = sourceTokenOptions(skipOrOptions, "skip");
1778
- return sourceForwardToken(sourceApplyTokenFilter(sourceItemsAfter(sourceTokenItems(this, options), this.getRange(nodeOrToken)[1]), options), options);
1785
+ return tokenWindow(this, options, this.getRange(nodeOrToken)[1], null, false, true);
1779
1786
  }
1780
1787
 
1781
1788
  getTokensAfter(nodeOrToken, countOrOptions = 1) {
1782
- const options = sourceTokenOptions(countOrOptions, "count");
1783
- return sourceForwardTokens(sourceApplyTokenFilter(sourceItemsAfter(sourceTokenItems(this, options), this.getRange(nodeOrToken)[1]), options), options);
1789
+ const options = sourceCountedTokenOptions(countOrOptions);
1790
+ return tokenWindow(this, options, this.getRange(nodeOrToken)[1], null, false, false);
1784
1791
  }
1785
1792
 
1786
1793
  getTokensBetween(left, right, optionsOrCount) {
1787
- const options = sourceTokenOptions(optionsOrCount, "count");
1788
- return sourceForwardTokens(sourceTokensBetween(this, left, right, options), options);
1794
+ const options = sourceCountedTokenOptions(optionsOrCount);
1795
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, false);
1789
1796
  }
1790
1797
 
1791
1798
  getFirstTokenBetween(left, right, skipOrOptions = 0) {
1792
1799
  const options = sourceTokenOptions(skipOrOptions, "skip");
1793
- return sourceForwardToken(sourceTokensBetween(this, left, right, options), options);
1800
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, true);
1794
1801
  }
1795
1802
 
1796
1803
  getFirstTokensBetween(left, right, countOrOptions = 1) {
1797
- const options = sourceTokenOptions(countOrOptions, "count");
1798
- return sourceForwardTokens(sourceTokensBetween(this, left, right, options), options);
1804
+ const options = sourceCountedTokenOptions(countOrOptions);
1805
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], false, false);
1799
1806
  }
1800
1807
 
1801
1808
  getLastTokenBetween(left, right, skipOrOptions = 0) {
1802
1809
  const options = sourceTokenOptions(skipOrOptions, "skip");
1803
- return sourceBackwardToken(sourceTokensBetween(this, left, right, options), options);
1810
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], true, true);
1804
1811
  }
1805
1812
 
1806
1813
  getLastTokensBetween(left, right, countOrOptions = 1) {
1807
- const options = sourceTokenOptions(countOrOptions, "count");
1808
- return sourceBackwardTokens(sourceTokensBetween(this, left, right, options), options);
1814
+ const options = sourceCountedTokenOptions(countOrOptions);
1815
+ return tokenWindow(this, options, this.getRange(left)[1], this.getRange(right)[0], true, false);
1809
1816
  }
1810
1817
 
1811
1818
  getTokenByRangeStart(index, options = {}) {
@@ -1813,11 +1820,11 @@ export class SourceCode {
1813
1820
  }
1814
1821
 
1815
1822
  getTokenOrCommentBefore(nodeOrToken) {
1816
- return sourceItemsBefore(sourceTokensAndComments(this), this.getRange(nodeOrToken)[0]).at(-1) ?? null;
1823
+ return this.getTokenBefore(nodeOrToken, { includeComments: true });
1817
1824
  }
1818
1825
 
1819
1826
  getTokenOrCommentAfter(nodeOrToken) {
1820
- return sourceItemsAfter(sourceTokensAndComments(this), this.getRange(nodeOrToken)[1])[0] ?? null;
1827
+ return this.getTokenAfter(nodeOrToken, { includeComments: true });
1821
1828
  }
1822
1829
 
1823
1830
  getCommentsBefore(nodeOrToken) {
@@ -1990,11 +1997,12 @@ function sourceTokensAndComments(sourceCode) {
1990
1997
  return [...sourceCode.tokens, ...sourceCode.comments].sort((left, right) => (left.range?.[0] ?? 0) - (right.range?.[0] ?? 0));
1991
1998
  }
1992
1999
 
1993
- function sourceTokensBetween(sourceCode, left, right, options) {
1994
- return sourceApplyTokenFilter(
1995
- sourceItemsBetween(sourceTokenItems(sourceCode, options), sourceCode.getRange(left)[1], sourceCode.getRange(right)[0]),
1996
- options
1997
- );
2000
+ function sourceNodeTokenWindow(sourceCode, node, options, backwards, single) {
2001
+ if (!node) {
2002
+ return selectTokenItems(sourceTokenItems(sourceCode, options), options, backwards, single);
2003
+ }
2004
+ const range = expandSourceRange(sourceCode.getRange(node), options.beforeCount, options.afterCount, sourceCode.text.length);
2005
+ return tokenWindow(sourceCode, options, range[0], range[1], backwards, single);
1998
2006
  }
1999
2007
 
2000
2008
  function sourceTokenItems(sourceCode, options) {
@@ -2027,6 +2035,14 @@ function sourceTokenOptions(optionsOrNumber, numericKey) {
2027
2035
  };
2028
2036
  }
2029
2037
 
2038
+ // Adjacent/between token APIs default an omitted count to one, including
2039
+ // options objects; getFirstTokens/getLastTokens keep their unbounded form.
2040
+ function sourceCountedTokenOptions(value) {
2041
+ const options = sourceTokenOptions(value, "count");
2042
+ options.count ??= 1;
2043
+ return options;
2044
+ }
2045
+
2030
2046
  function sourceTokenCount(value, defaultValue) {
2031
2047
  return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : defaultValue;
2032
2048
  }
@@ -2035,33 +2051,6 @@ function sourceApplyTokenFilter(items, options) {
2035
2051
  return options.filter ? items.filter((item) => options.filter(item)) : items;
2036
2052
  }
2037
2053
 
2038
- function sourceForwardToken(items, optionsOrNumber) {
2039
- const options = sourceTokenOptions(optionsOrNumber, "skip");
2040
- return items[options.skip] ?? null;
2041
- }
2042
-
2043
- function sourceForwardTokens(items, optionsOrNumber) {
2044
- const options = sourceTokenOptions(optionsOrNumber, "count");
2045
- const start = options.skip;
2046
- const end = options.count === null ? undefined : start + options.count;
2047
- return items.slice(start, end);
2048
- }
2049
-
2050
- function sourceBackwardToken(items, optionsOrNumber) {
2051
- const options = sourceTokenOptions(optionsOrNumber, "skip");
2052
- return items.at(-(options.skip + 1)) ?? null;
2053
- }
2054
-
2055
- function sourceBackwardTokens(items, optionsOrNumber) {
2056
- const options = sourceTokenOptions(optionsOrNumber, "count");
2057
- if (options.count === 0) {
2058
- return [];
2059
- }
2060
- const end = options.skip === 0 ? undefined : -options.skip;
2061
- const available = end === undefined ? items : items.slice(0, end);
2062
- return options.count === null ? available : available.slice(-options.count);
2063
- }
2064
-
2065
2054
  function sourceNodeByRangeIndex(node, index, seen = new Set()) {
2066
2055
  if (!node || typeof node !== "object" || seen.has(node)) {
2067
2056
  return null;
@@ -3486,6 +3475,11 @@ function appendPluginRuleDiagnostics(report, options, { perFileConfig = true } =
3486
3475
  }
3487
3476
 
3488
3477
  function nativeFlatConfigOptions(options) {
3478
+ // lintText stores source in a temporary file. Resolve its configuration
3479
+ // against the logical filename before passing the materialized config to native.
3480
+ if (options.pluginTextFilePath !== undefined) {
3481
+ return undefined;
3482
+ }
3489
3483
  if (defersLegacyIgnores(options)) {
3490
3484
  // A legacy config may cascade from a nested directory even with baseConfig.
3491
3485
  return undefined;
@@ -3653,7 +3647,7 @@ function nativeConfigRunsForFiles(paths, options) {
3653
3647
  let hasConfigSource = Boolean(options.baseConfig || options.overrideConfig);
3654
3648
  for (const filePath of paths) {
3655
3649
  const matchPath = paths.length === 1
3656
- ? options.filePath ?? options.filename ?? filePath
3650
+ ? options.filePath ?? options.filename ?? options.pluginTextFilePath ?? filePath
3657
3651
  : filePath;
3658
3652
  const fileConfigPath = options.noConfig ? undefined : configPathForFile(options, matchPath);
3659
3653
  const configured = Boolean(options.baseConfig || options.overrideConfig || fileConfigPath || legacyConfigForFile(options, matchPath));
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // The native lint engine keeps its own AST in Zig. Plugin rules are JavaScript
6
6
  // and expect the ESTree shape that ESLint hands to `create(context)`, so this
7
- // module parses the file a second time with yuku-parser (the same parser the
7
+ // module parses the file a second time with yuku (the same parser the
8
8
  // engine is built on), then decorates the tree with `range`, `loc`, `parent`,
9
9
  // a token stream, comments, and a lazily computed scope manager.
10
10
  //
@@ -70,12 +70,12 @@ function loadYuku() {
70
70
  throw yukuLoadError;
71
71
  }
72
72
  try {
73
- // yuku-parser is an ES module. Loading it synchronously from CommonJS
73
+ // yuku is an ES module. Loading it synchronously from CommonJS
74
74
  // relies on require(esm), which Node.js 20.19+ and 22.12+ support.
75
- yukuModule = require("yuku-parser");
75
+ yukuModule = require("yuku");
76
76
  } catch (error) {
77
77
  yukuLoadError = new Error(
78
- "utoo-lint: unable to load yuku-parser, which is required to run ESLint plugin rules. " +
78
+ "utoo-lint: unable to load yuku, which is required to run ESLint plugin rules. " +
79
79
  "Plugin rules need Node.js 20.19 or newer (or 22.12 or newer). " +
80
80
  `Underlying error: ${error?.message ?? error}`
81
81
  );
@@ -192,13 +192,14 @@ function decorateNode(node, locate) {
192
192
  node.loc = { start: locate(node.start), end: locate(node.end) };
193
193
  }
194
194
 
195
- function normalizeTree(program, text, locate) {
195
+ function normalizeTree(program, text, locate, lang) {
196
196
  const stack = [[program, null]];
197
197
  while (stack.length > 0) {
198
198
  const [node, parent] = stack.pop();
199
- if (node.type === "TemplateElement") {
199
+ if (node.type === "TemplateElement" && !isTypeScriptLanguage(lang)) {
200
200
  // ESLint's parsers include the surrounding backticks and `${`/`}`
201
- // delimiters in a template element's range.
201
+ // delimiters in a template element's range. Yuku already includes those
202
+ // delimiters in TypeScript trees.
202
203
  node.start -= 1;
203
204
  node.end += node.tail ? 1 : 2;
204
205
  }
@@ -607,12 +608,14 @@ function parseForESLint(text, options = {}) {
607
608
  const ecmaFeatures = options.ecmaFeatures ?? {};
608
609
  const lang = languageForFile(filePath, ecmaFeatures);
609
610
  const sourceType = sourceTypeForFile(filePath, options.sourceType);
610
- const result = parse(text, { lang, sourceType, preserveParens: false });
611
- const program = result.program;
611
+ const file = parse(text, { lang, sourceType, preserveParens: false });
612
+ const program = file.program;
612
613
  const locate = createLocator(text);
613
614
 
614
- normalizeTree(program, text, locate);
615
- program.comments = normalizeComments(program, result.comments ?? [], text, locate);
615
+ normalizeTree(program, text, locate, lang);
616
+ program.comments = normalizeComments(program, file.comments, text, locate);
617
+ // SourceFile.tokens requires tokens: true and uses Yuku's TokenList API.
618
+ // Build the ESLint token array from the normalized tree instead.
616
619
  program.tokens = buildTokens(program, text, locate, lang);
617
620
 
618
621
  return {
@@ -620,7 +623,7 @@ function parseForESLint(text, options = {}) {
620
623
  scopeManager: createScopeManagerFactory(program, { ...options, lang, sourceType, ecmaFeatures }),
621
624
  visitorKeys: VISITOR_KEYS,
622
625
  services: {},
623
- diagnostics: result.diagnostics ?? []
626
+ diagnostics: file.diagnostics
624
627
  };
625
628
  }
626
629
 
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+
3
+ function inRange(item, start, end) {
4
+ return item.range && (start === null || item.range[0] >= start) && (end === null || item.range[1] <= end);
5
+ }
6
+
7
+ function select(items, options, backwards, single) {
8
+ const filtered = options.filter ? items.filter((item) => options.filter(item)) : items;
9
+ if (single) {
10
+ return backwards ? filtered.at(-(options.skip + 1)) ?? null : filtered[options.skip] ?? null;
11
+ }
12
+ if (!backwards) {
13
+ return filtered.slice(options.skip, options.count === null ? undefined : options.skip + options.count);
14
+ }
15
+ if (options.count === 0) {
16
+ return [];
17
+ }
18
+ if (options.count === null && options.skip === 0) {
19
+ return filtered;
20
+ }
21
+ const end = Math.max(0, filtered.length - options.skip);
22
+ const start = options.count === null ? 0 : Math.max(0, end - options.count);
23
+ return filtered.slice(start, end);
24
+ }
25
+
26
+ function collect(sourceCode, options, start, end) {
27
+ if (!options.includeComments) {
28
+ return sourceCode.tokens.filter((item) => inRange(item, start, end));
29
+ }
30
+ // Sort the live arrays for each query: callers can replace, reorder, or edit
31
+ // tokens and comments in place. Equal starts keep tokens before comments.
32
+ const items = [...sourceCode.tokens, ...sourceCode.comments];
33
+ items.sort((left, right) => (left.range?.[0] ?? 0) - (right.range?.[0] ?? 0));
34
+ return items.filter((item) => inRange(item, start, end));
35
+ }
36
+
37
+ /** Select a token window without assuming the public arrays remain sorted. */
38
+ function tokenWindow(sourceCode, options, start, end, backwards, single) {
39
+ // A predicate must see every candidate in the same order, even when only
40
+ // one token is requested or count is zero.
41
+ if (options.filter) {
42
+ return select(collect(sourceCode, options, start, end), options, backwards, single);
43
+ }
44
+
45
+ if (options.includeComments) {
46
+ if (!single || options.skip !== 0) {
47
+ return select(collect(sourceCode, options, start, end), options, backwards, single);
48
+ }
49
+ // The nearest item in the sorted combined stream can be selected without
50
+ // allocating or sorting it. Scan both arrays to also handle unsorted input.
51
+ let found = null;
52
+ let foundStart = backwards ? -Infinity : Infinity;
53
+ for (const items of [sourceCode.tokens, sourceCode.comments]) {
54
+ for (const item of items) {
55
+ const itemStart = item.range?.[0] ?? 0;
56
+ if (typeof itemStart !== "number" || !Number.isFinite(itemStart)) {
57
+ return select(collect(sourceCode, options, start, end), options, backwards, single);
58
+ }
59
+ if (!inRange(item, start, end)) {
60
+ continue;
61
+ }
62
+ if (backwards ? itemStart >= foundStart : itemStart < foundStart) {
63
+ found = item;
64
+ foundStart = itemStart;
65
+ }
66
+ }
67
+ }
68
+ return found;
69
+ }
70
+
71
+ const items = sourceCode.tokens;
72
+ const result = [];
73
+ const count = single ? 1 : options.count;
74
+ if (count === 0) {
75
+ return result;
76
+ }
77
+ let skip = options.skip;
78
+ for (let index = backwards ? items.length - 1 : 0; backwards ? index >= 0 : index < items.length; index += backwards ? -1 : 1) {
79
+ if (!(index in items) || !inRange(items[index], start, end)) {
80
+ continue;
81
+ }
82
+ if (skip > 0) {
83
+ skip -= 1;
84
+ continue;
85
+ }
86
+ if (single) {
87
+ return items[index];
88
+ }
89
+ result.push(items[index]);
90
+ if (count !== null && result.length === count) {
91
+ break;
92
+ }
93
+ }
94
+ return single ? null : backwards ? result.reverse() : result;
95
+ }
96
+
97
+ module.exports = { tokenWindow, selectTokenItems: select };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.4.9",
3
+ "version": "0.4.11",
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.9",
78
- "@utoo/lint-darwin-x64": "0.4.9",
79
- "@utoo/lint-linux-arm64": "0.4.9",
80
- "@utoo/lint-linux-x64": "0.4.9",
81
- "@utoo/lint-win32-x64": "0.4.9"
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"
82
82
  },
83
83
  "dependencies": {
84
84
  "@eslint/eslintrc": "2.1.4",
@@ -88,7 +88,7 @@
88
88
  "eslint-scope": "9.1.2",
89
89
  "esquery": "1.7.0",
90
90
  "jiti": "2.7.0",
91
- "yuku-parser": "0.18.0"
91
+ "yuku": "0.22.1"
92
92
  },
93
93
  "publishConfig": {
94
94
  "access": "public",