@utoo/lint 0.4.9 → 0.4.10
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 +49 -55
- package/index.js +49 -55
- package/lib/estree-parser.cjs +15 -12
- package/lib/source-code-tokens.cjs +97 -0
- package/package.json +7 -7
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
|
-
|
|
3523
|
-
|
|
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
|
|
3579
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), false, true);
|
|
3568
3580
|
}
|
|
3569
3581
|
|
|
3570
3582
|
getFirstTokens(node, countOrOptions = 1) {
|
|
3571
|
-
return
|
|
3583
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), false, false);
|
|
3572
3584
|
}
|
|
3573
3585
|
|
|
3574
3586
|
getLastToken(node, skipOrOptions = 0) {
|
|
3575
|
-
return
|
|
3587
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), true, true);
|
|
3576
3588
|
}
|
|
3577
3589
|
|
|
3578
3590
|
getLastTokens(node, countOrOptions = 1) {
|
|
3579
|
-
return
|
|
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
|
|
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 =
|
|
3589
|
-
return
|
|
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
|
|
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 =
|
|
3599
|
-
return
|
|
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 =
|
|
3604
|
-
return
|
|
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
|
|
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 =
|
|
3614
|
-
return
|
|
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
|
|
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 =
|
|
3624
|
-
return
|
|
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
|
|
3644
|
+
return this.getTokenBefore(nodeOrToken, { includeComments: true });
|
|
3633
3645
|
}
|
|
3634
3646
|
|
|
3635
3647
|
getTokenOrCommentAfter(nodeOrToken) {
|
|
3636
|
-
return
|
|
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
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
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
|
-
|
|
1707
|
-
|
|
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
|
|
1758
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), false, true);
|
|
1752
1759
|
}
|
|
1753
1760
|
|
|
1754
1761
|
getFirstTokens(node, countOrOptions = 1) {
|
|
1755
|
-
return
|
|
1762
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(countOrOptions, "count"), false, false);
|
|
1756
1763
|
}
|
|
1757
1764
|
|
|
1758
1765
|
getLastToken(node, skipOrOptions = 0) {
|
|
1759
|
-
return
|
|
1766
|
+
return sourceNodeTokenWindow(this, node, sourceTokenOptions(skipOrOptions, "skip"), true, true);
|
|
1760
1767
|
}
|
|
1761
1768
|
|
|
1762
1769
|
getLastTokens(node, countOrOptions = 1) {
|
|
1763
|
-
return
|
|
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
|
|
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 =
|
|
1773
|
-
return
|
|
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
|
|
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 =
|
|
1783
|
-
return
|
|
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 =
|
|
1788
|
-
return
|
|
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
|
|
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 =
|
|
1798
|
-
return
|
|
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
|
|
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 =
|
|
1808
|
-
return
|
|
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
|
|
1823
|
+
return this.getTokenBefore(nodeOrToken, { includeComments: true });
|
|
1817
1824
|
}
|
|
1818
1825
|
|
|
1819
1826
|
getTokenOrCommentAfter(nodeOrToken) {
|
|
1820
|
-
return
|
|
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
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
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));
|
package/lib/estree-parser.cjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
75
|
+
yukuModule = require("yuku");
|
|
76
76
|
} catch (error) {
|
|
77
77
|
yukuLoadError = new Error(
|
|
78
|
-
"utoo-lint: unable to load yuku
|
|
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
|
|
611
|
-
const 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,
|
|
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:
|
|
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.
|
|
3
|
+
"version": "0.4.10",
|
|
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.
|
|
78
|
-
"@utoo/lint-darwin-x64": "0.4.
|
|
79
|
-
"@utoo/lint-linux-arm64": "0.4.
|
|
80
|
-
"@utoo/lint-linux-x64": "0.4.
|
|
81
|
-
"@utoo/lint-win32-x64": "0.4.
|
|
77
|
+
"@utoo/lint-darwin-arm64": "0.4.10",
|
|
78
|
+
"@utoo/lint-darwin-x64": "0.4.10",
|
|
79
|
+
"@utoo/lint-linux-arm64": "0.4.10",
|
|
80
|
+
"@utoo/lint-linux-x64": "0.4.10",
|
|
81
|
+
"@utoo/lint-win32-x64": "0.4.10"
|
|
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
|
|
91
|
+
"yuku": "0.22.1"
|
|
92
92
|
},
|
|
93
93
|
"publishConfig": {
|
|
94
94
|
"access": "public",
|