@rslint/core 0.6.2 → 0.6.4
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/dist/207.js +897 -0
- package/dist/cli.js +1 -894
- package/dist/config-loader.js +33 -2
- package/dist/eslint-plugin/index.d.ts +1 -0
- package/dist/eslint-plugin/index.js +2 -1
- package/dist/eslint-plugin/lint-worker.js +2 -1
- package/dist/index.d.ts +93 -387
- package/dist/index.js +262 -140
- package/dist/internal.d.ts +135 -0
- package/dist/internal.js +167 -0
- package/dist/service.d.ts +33 -27
- package/dist/service.js +7 -20
- package/package.json +19 -21
- package/dist/34.js +0 -33
- package/dist/browser.d.ts +0 -52
- package/dist/browser.js +0 -115
package/dist/index.js
CHANGED
|
@@ -1,121 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { NodeRslintService } from "./internal.js";
|
|
4
|
+
import { loadConfigFile, normalizeConfig } from "./config-loader.js";
|
|
5
|
+
import { findJSConfigUp, glob } from "./207.js";
|
|
3
6
|
import { RSLintService } from "./service.js";
|
|
4
|
-
const
|
|
5
|
-
function resolveRslintBinary() {
|
|
6
|
-
const arch = process.arch;
|
|
7
|
-
const tuples = 'linux' === process.platform ? [
|
|
8
|
-
`linux-${arch}-gnu`,
|
|
9
|
-
`linux-${arch}-musl`
|
|
10
|
-
] : 'win32' === process.platform ? [
|
|
11
|
-
`win32-${arch}-msvc`
|
|
12
|
-
] : [
|
|
13
|
-
`${process.platform}-${arch}`
|
|
14
|
-
];
|
|
15
|
-
for (const tuple of tuples)try {
|
|
16
|
-
return node_require.resolve(`@rslint/native-${tuple}/bin`);
|
|
17
|
-
} catch {}
|
|
18
|
-
throw new Error(`rslint: no native binary for ${process.platform}-${arch} (looked for @rslint/native-{${tuples.join(',')}})`);
|
|
19
|
-
}
|
|
20
|
-
class NodeRslintService {
|
|
21
|
-
nextMessageId;
|
|
22
|
-
pendingMessages;
|
|
23
|
-
rslintPath;
|
|
24
|
-
process;
|
|
25
|
-
chunks;
|
|
26
|
-
chunkSize;
|
|
27
|
-
expectedSize;
|
|
28
|
-
constructor(options = {}){
|
|
29
|
-
this.nextMessageId = 1;
|
|
30
|
-
this.pendingMessages = new Map();
|
|
31
|
-
this.rslintPath = options.rslintPath || resolveRslintBinary();
|
|
32
|
-
this.process = spawn(this.rslintPath, [
|
|
33
|
-
'--api'
|
|
34
|
-
], {
|
|
35
|
-
stdio: [
|
|
36
|
-
'pipe',
|
|
37
|
-
'pipe',
|
|
38
|
-
'inherit'
|
|
39
|
-
],
|
|
40
|
-
cwd: options.workingDirectory || process.cwd(),
|
|
41
|
-
env: {
|
|
42
|
-
...process.env
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
this.process.stdout.on('data', (data)=>{
|
|
46
|
-
this.handleChunk(data);
|
|
47
|
-
});
|
|
48
|
-
this.chunks = [];
|
|
49
|
-
this.chunkSize = 0;
|
|
50
|
-
this.expectedSize = null;
|
|
51
|
-
}
|
|
52
|
-
async sendMessage(kind, data) {
|
|
53
|
-
return new Promise((resolve, reject)=>{
|
|
54
|
-
const id = this.nextMessageId++;
|
|
55
|
-
const message = {
|
|
56
|
-
id,
|
|
57
|
-
kind,
|
|
58
|
-
data
|
|
59
|
-
};
|
|
60
|
-
this.pendingMessages.set(id, {
|
|
61
|
-
resolve,
|
|
62
|
-
reject
|
|
63
|
-
});
|
|
64
|
-
const json = JSON.stringify(message);
|
|
65
|
-
const jsonBuffer = Buffer.from(json, 'utf8');
|
|
66
|
-
const length = Buffer.alloc(4);
|
|
67
|
-
length.writeUInt32LE(jsonBuffer.length, 0);
|
|
68
|
-
this.process.stdin.write(Buffer.concat([
|
|
69
|
-
length,
|
|
70
|
-
jsonBuffer
|
|
71
|
-
]));
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
handleChunk(chunk) {
|
|
75
|
-
this.chunks.push(chunk);
|
|
76
|
-
this.chunkSize += chunk.length;
|
|
77
|
-
while(true){
|
|
78
|
-
if (null === this.expectedSize) {
|
|
79
|
-
if (this.chunkSize < 4) return;
|
|
80
|
-
const combined = Buffer.concat(this.chunks);
|
|
81
|
-
this.expectedSize = combined.readUInt32LE(0);
|
|
82
|
-
this.chunks = [
|
|
83
|
-
combined.subarray(4)
|
|
84
|
-
];
|
|
85
|
-
this.chunkSize -= 4;
|
|
86
|
-
}
|
|
87
|
-
if (this.chunkSize < this.expectedSize) return;
|
|
88
|
-
const combined = Buffer.concat(this.chunks);
|
|
89
|
-
const message = combined.subarray(0, this.expectedSize).toString('utf8');
|
|
90
|
-
try {
|
|
91
|
-
const parsed = JSON.parse(message);
|
|
92
|
-
this.handleMessage(parsed);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
console.error('Error parsing message:', err);
|
|
95
|
-
}
|
|
96
|
-
this.chunks = [
|
|
97
|
-
combined.subarray(this.expectedSize)
|
|
98
|
-
];
|
|
99
|
-
this.chunkSize = this.chunks[0].length;
|
|
100
|
-
this.expectedSize = null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
handleMessage(message) {
|
|
104
|
-
const { id, kind, data } = message;
|
|
105
|
-
const pending = this.pendingMessages.get(id);
|
|
106
|
-
if (!pending) return;
|
|
107
|
-
this.pendingMessages.delete(id);
|
|
108
|
-
if ('error' === kind) pending.reject(new Error(data.message));
|
|
109
|
-
else pending.resolve(data);
|
|
110
|
-
}
|
|
111
|
-
terminate() {
|
|
112
|
-
if (this.process && !this.process.killed) {
|
|
113
|
-
this.process.stdin.end();
|
|
114
|
-
this.process.kill();
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
const base = {
|
|
7
|
+
const typescript_base = {
|
|
119
8
|
files: [
|
|
120
9
|
'**/*.ts',
|
|
121
10
|
'**/*.tsx',
|
|
@@ -127,7 +16,7 @@ const base = {
|
|
|
127
16
|
]
|
|
128
17
|
};
|
|
129
18
|
const recommended = {
|
|
130
|
-
...
|
|
19
|
+
...typescript_base,
|
|
131
20
|
languageOptions: {
|
|
132
21
|
parserOptions: {
|
|
133
22
|
projectService: true
|
|
@@ -347,7 +236,8 @@ const promise_recommended = {
|
|
|
347
236
|
'promise/no-return-wrap': 'error',
|
|
348
237
|
'promise/param-names': 'error',
|
|
349
238
|
'promise/catch-or-return': 'error',
|
|
350
|
-
'promise/avoid-new': 'off'
|
|
239
|
+
'promise/avoid-new': 'off',
|
|
240
|
+
'promise/valid-params': 'warn'
|
|
351
241
|
}
|
|
352
242
|
};
|
|
353
243
|
const jest_recommended = {
|
|
@@ -361,6 +251,7 @@ const jest_recommended = {
|
|
|
361
251
|
'jest/no-deprecated-functions': 'error',
|
|
362
252
|
'jest/no-disabled-tests': 'warn',
|
|
363
253
|
'jest/no-done-callback': 'error',
|
|
254
|
+
'jest/no-export': 'error',
|
|
364
255
|
'jest/no-focused-tests': 'error',
|
|
365
256
|
'jest/no-identical-title': 'error',
|
|
366
257
|
'jest/no-jasmine-globals': 'error',
|
|
@@ -560,7 +451,7 @@ const jsx_a11y_recommended = {
|
|
|
560
451
|
};
|
|
561
452
|
const ts = {
|
|
562
453
|
configs: {
|
|
563
|
-
base:
|
|
454
|
+
base: typescript_base,
|
|
564
455
|
recommended: recommended
|
|
565
456
|
}
|
|
566
457
|
};
|
|
@@ -605,26 +496,257 @@ const jsxA11yPlugin = {
|
|
|
605
496
|
recommended: jsx_a11y_recommended
|
|
606
497
|
}
|
|
607
498
|
};
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
499
|
+
function _to_primitive(input, hint) {
|
|
500
|
+
if ("object" !== _type_of(input) || null === input) return input;
|
|
501
|
+
var prim = input[Symbol.toPrimitive];
|
|
502
|
+
if (void 0 !== prim) {
|
|
503
|
+
var res = prim.call(input, hint || "default");
|
|
504
|
+
if ("object" !== _type_of(res)) return res;
|
|
505
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
506
|
+
}
|
|
507
|
+
return ("string" === hint ? String : Number)(input);
|
|
508
|
+
}
|
|
509
|
+
function _to_property_key(arg) {
|
|
510
|
+
var key = _to_primitive(arg, "string");
|
|
511
|
+
return "symbol" === _type_of(key) ? key : String(key);
|
|
512
|
+
}
|
|
513
|
+
function _type_of(obj) {
|
|
514
|
+
return obj && "u" > typeof Symbol && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
515
|
+
}
|
|
516
|
+
let _computedKey;
|
|
517
|
+
_computedKey = _to_property_key(Symbol.asyncDispose);
|
|
518
|
+
class Rslint {
|
|
519
|
+
#service;
|
|
520
|
+
#cwd;
|
|
521
|
+
#overrideConfig;
|
|
522
|
+
#overrideConfigFile;
|
|
523
|
+
#fix;
|
|
524
|
+
#virtualFiles;
|
|
525
|
+
constructor(options = {}){
|
|
526
|
+
this.#cwd = options.cwd ? node_path.resolve(options.cwd) : process.cwd();
|
|
527
|
+
this.#overrideConfig = options.overrideConfig;
|
|
528
|
+
this.#overrideConfigFile = options.overrideConfigFile;
|
|
529
|
+
this.#fix = options.fix ?? false;
|
|
530
|
+
this.#virtualFiles = options.virtualFiles;
|
|
531
|
+
this.#service = new RSLintService(new NodeRslintService());
|
|
532
|
+
}
|
|
533
|
+
async lintText(code, options = {}) {
|
|
534
|
+
const filePath = node_path.resolve(this.#cwd, options.filePath ?? '__text__.ts');
|
|
535
|
+
const { config, configDirectory } = await this.#resolveConfig(node_path.dirname(filePath));
|
|
536
|
+
const response = await this.#service.lint({
|
|
537
|
+
config,
|
|
538
|
+
configDirectory,
|
|
539
|
+
workingDirectory: this.#cwd,
|
|
540
|
+
files: [
|
|
541
|
+
filePath
|
|
542
|
+
],
|
|
543
|
+
fileContents: {
|
|
544
|
+
...this.#resolveOverlay(),
|
|
545
|
+
[filePath]: code
|
|
546
|
+
},
|
|
547
|
+
fix: this.#fix
|
|
548
|
+
});
|
|
549
|
+
const results = this.#toLintResults(response, configDirectory, [
|
|
550
|
+
filePath
|
|
551
|
+
], {
|
|
552
|
+
[filePath]: code
|
|
553
|
+
});
|
|
554
|
+
const primary = results.filter((r)=>r.filePath === filePath);
|
|
555
|
+
if (null == options.filePath) {
|
|
556
|
+
for (const r of primary)if (r.filePath === filePath) r.filePath = '<text>';
|
|
557
|
+
}
|
|
558
|
+
return primary;
|
|
559
|
+
}
|
|
560
|
+
async lintFiles(patterns) {
|
|
561
|
+
const globs = Array.isArray(patterns) ? patterns : [
|
|
562
|
+
patterns
|
|
563
|
+
];
|
|
564
|
+
const matched = await glob(globs, {
|
|
565
|
+
cwd: this.#cwd,
|
|
566
|
+
absolute: true,
|
|
567
|
+
onlyFiles: true
|
|
568
|
+
});
|
|
569
|
+
const files = matched.map((f)=>node_path.normalize(f));
|
|
570
|
+
if (0 === files.length) return [];
|
|
571
|
+
const { config, configDirectory } = await this.#resolveConfig(this.#cwd);
|
|
572
|
+
const response = await this.#service.lint({
|
|
573
|
+
config,
|
|
574
|
+
configDirectory,
|
|
575
|
+
workingDirectory: this.#cwd,
|
|
576
|
+
files,
|
|
577
|
+
fileContents: this.#resolveOverlay(),
|
|
578
|
+
fix: this.#fix
|
|
579
|
+
});
|
|
580
|
+
const contents = {};
|
|
581
|
+
const bomFiles = new Set();
|
|
582
|
+
for (const d of response.diagnostics ?? []){
|
|
583
|
+
const abs = node_path.isAbsolute(d.filePath) ? node_path.normalize(d.filePath) : node_path.resolve(configDirectory, d.filePath);
|
|
584
|
+
if (!(abs in contents)) try {
|
|
585
|
+
const raw = await readFile(abs, 'utf8');
|
|
586
|
+
if (0xfeff === raw.charCodeAt(0)) {
|
|
587
|
+
bomFiles.add(abs);
|
|
588
|
+
contents[abs] = raw.slice(1);
|
|
589
|
+
} else contents[abs] = raw;
|
|
590
|
+
} catch {}
|
|
591
|
+
}
|
|
592
|
+
const linted = response.lintedFiles ? response.lintedFiles.map((f)=>node_path.isAbsolute(f) ? node_path.normalize(f) : node_path.resolve(configDirectory, f)) : files;
|
|
593
|
+
return this.#toLintResults(response, configDirectory, linted, contents, bomFiles);
|
|
594
|
+
}
|
|
595
|
+
static async outputFixes(results) {
|
|
596
|
+
await Promise.all(results.map(async (r)=>{
|
|
597
|
+
if ('string' == typeof r.output && node_path.isAbsolute(r.filePath)) await writeFile(r.filePath, r.output);
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
async close() {
|
|
601
|
+
await this.#service.close();
|
|
602
|
+
}
|
|
603
|
+
async [_computedKey]() {
|
|
604
|
+
await this.close();
|
|
605
|
+
}
|
|
606
|
+
#resolveOverlay() {
|
|
607
|
+
if (!this.#virtualFiles) return;
|
|
608
|
+
const resolved = {};
|
|
609
|
+
for (const [p, content] of Object.entries(this.#virtualFiles))resolved[node_path.resolve(this.#cwd, p)] = content;
|
|
610
|
+
return resolved;
|
|
611
|
+
}
|
|
612
|
+
async #resolveConfig(fromDir) {
|
|
613
|
+
let base = [];
|
|
614
|
+
let configDirectory = this.#cwd;
|
|
615
|
+
if (true === this.#overrideConfigFile) ;
|
|
616
|
+
else if ('string' == typeof this.#overrideConfigFile) {
|
|
617
|
+
const configPath = node_path.resolve(this.#cwd, this.#overrideConfigFile);
|
|
618
|
+
base = normalizeConfig(await loadConfigFile(configPath));
|
|
619
|
+
configDirectory = node_path.dirname(configPath);
|
|
620
|
+
} else {
|
|
621
|
+
const configPath = findJSConfigUp(fromDir);
|
|
622
|
+
if (configPath) {
|
|
623
|
+
base = normalizeConfig(await loadConfigFile(configPath));
|
|
624
|
+
configDirectory = node_path.dirname(configPath);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (null != this.#overrideConfig) {
|
|
628
|
+
const override = Array.isArray(this.#overrideConfig) ? this.#overrideConfig : [
|
|
629
|
+
this.#overrideConfig
|
|
630
|
+
];
|
|
631
|
+
base = [
|
|
632
|
+
...base,
|
|
633
|
+
...normalizeConfig(override)
|
|
634
|
+
];
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
config: base,
|
|
638
|
+
configDirectory
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
#toLintResults(response, configDirectory, files, contents, bomFiles) {
|
|
642
|
+
const toAbs = (p)=>node_path.isAbsolute(p) ? node_path.normalize(p) : node_path.resolve(configDirectory, p);
|
|
643
|
+
const byFile = new Map();
|
|
644
|
+
for (const f of files)byFile.set(node_path.normalize(f), []);
|
|
645
|
+
for (const d of response.diagnostics ?? []){
|
|
646
|
+
const abs = toAbs(d.filePath);
|
|
647
|
+
let messages = byFile.get(abs);
|
|
648
|
+
if (!messages) {
|
|
649
|
+
messages = [];
|
|
650
|
+
byFile.set(abs, messages);
|
|
651
|
+
}
|
|
652
|
+
messages.push(toLintMessage(d, contents?.[abs]));
|
|
653
|
+
}
|
|
654
|
+
const outputByAbs = new Map();
|
|
655
|
+
for (const [rel, fixed] of Object.entries(response.output ?? {}))outputByAbs.set(toAbs(rel), fixed);
|
|
656
|
+
const results = [];
|
|
657
|
+
for (const [filePath, messages] of byFile){
|
|
658
|
+
let errorCount = 0;
|
|
659
|
+
let warningCount = 0;
|
|
660
|
+
let fixableErrorCount = 0;
|
|
661
|
+
let fixableWarningCount = 0;
|
|
662
|
+
for (const m of messages)if (2 === m.severity) {
|
|
663
|
+
errorCount++;
|
|
664
|
+
if (m.fix) fixableErrorCount++;
|
|
665
|
+
} else {
|
|
666
|
+
warningCount++;
|
|
667
|
+
if (m.fix) fixableWarningCount++;
|
|
668
|
+
}
|
|
669
|
+
const result = {
|
|
670
|
+
filePath,
|
|
671
|
+
messages,
|
|
672
|
+
errorCount,
|
|
673
|
+
warningCount,
|
|
674
|
+
fixableErrorCount,
|
|
675
|
+
fixableWarningCount
|
|
676
|
+
};
|
|
677
|
+
const output = outputByAbs.get(filePath);
|
|
678
|
+
if (void 0 !== output) result.output = bomFiles?.has(filePath) ? '\uFEFF' + output : output;
|
|
679
|
+
results.push(result);
|
|
680
|
+
}
|
|
681
|
+
return results;
|
|
682
|
+
}
|
|
615
683
|
}
|
|
616
|
-
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
684
|
+
function toLintMessage(d, sourceText) {
|
|
685
|
+
const message = {
|
|
686
|
+
ruleId: d.ruleName || null,
|
|
687
|
+
severity: 'error' === d.severity ? 2 : 1,
|
|
688
|
+
message: d.message,
|
|
689
|
+
line: d.range.start.line,
|
|
690
|
+
column: d.range.start.column,
|
|
691
|
+
endLine: d.range.end.line,
|
|
692
|
+
endColumn: d.range.end.column
|
|
693
|
+
};
|
|
694
|
+
if (d.messageId) message.messageId = d.messageId;
|
|
695
|
+
const fix = mergeFixes(d.fixes, sourceText);
|
|
696
|
+
if (fix) message.fix = fix;
|
|
697
|
+
if (d.suggestions && d.suggestions.length > 0) message.suggestions = d.suggestions.map((s)=>{
|
|
698
|
+
const sFix = mergeFixes(s.fixes, sourceText);
|
|
699
|
+
return {
|
|
700
|
+
messageId: s.messageId,
|
|
701
|
+
...s.data ? {
|
|
702
|
+
data: s.data
|
|
703
|
+
} : {},
|
|
704
|
+
desc: s.message,
|
|
705
|
+
fix: sFix ?? {
|
|
706
|
+
range: [
|
|
707
|
+
0,
|
|
708
|
+
0
|
|
709
|
+
],
|
|
710
|
+
text: ''
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
return message;
|
|
621
715
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
716
|
+
function mergeFixes(fixes, sourceText) {
|
|
717
|
+
if (!fixes || 0 === fixes.length) return;
|
|
718
|
+
if (1 === fixes.length) return {
|
|
719
|
+
range: [
|
|
720
|
+
fixes[0].startPos,
|
|
721
|
+
fixes[0].endPos
|
|
722
|
+
],
|
|
723
|
+
text: fixes[0].text
|
|
724
|
+
};
|
|
725
|
+
const sorted = [
|
|
726
|
+
...fixes
|
|
727
|
+
].sort((a, b)=>a.startPos - b.startPos || a.endPos - b.endPos);
|
|
728
|
+
const start = sorted[0].startPos;
|
|
729
|
+
const end = sorted[sorted.length - 1].endPos;
|
|
730
|
+
if (void 0 === sourceText) return {
|
|
731
|
+
range: [
|
|
732
|
+
sorted[0].startPos,
|
|
733
|
+
sorted[0].endPos
|
|
734
|
+
],
|
|
735
|
+
text: sorted[0].text
|
|
736
|
+
};
|
|
737
|
+
let text = '';
|
|
738
|
+
let lastPos = start;
|
|
739
|
+
for (const f of sorted)if (!(f.startPos < lastPos)) {
|
|
740
|
+
text += sourceText.slice(lastPos, f.startPos) + f.text;
|
|
741
|
+
lastPos = f.endPos;
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
range: [
|
|
745
|
+
start,
|
|
746
|
+
end
|
|
747
|
+
],
|
|
748
|
+
text
|
|
749
|
+
};
|
|
627
750
|
}
|
|
628
|
-
export {
|
|
629
|
-
export {
|
|
630
|
-
export { NodeRslintService, applyFixes, getAstInfo, importPlugin, jestPlugin, js, jsxA11yPlugin, lint, promisePlugin, reactHooksPlugin, reactPlugin, ts, unicornPlugin };
|
|
751
|
+
export { defineConfig, globalIgnores } from "./config-loader.js";
|
|
752
|
+
export { Rslint, importPlugin, jestPlugin, js, jsxA11yPlugin, promisePlugin, reactHooksPlugin, reactPlugin, ts, unicornPlugin };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export declare interface Diagnostic {
|
|
2
|
+
ruleName: string;
|
|
3
|
+
message: string;
|
|
4
|
+
messageId: string;
|
|
5
|
+
filePath: string;
|
|
6
|
+
range: Range;
|
|
7
|
+
severity?: string;
|
|
8
|
+
fixes?: Fix[];
|
|
9
|
+
suggestions?: Suggestion[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare interface Fix {
|
|
13
|
+
text: string;
|
|
14
|
+
startPos: number;
|
|
15
|
+
endPos: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One-shot convenience: spin up a Node-backed service, run a single lint
|
|
20
|
+
* request, then tear it down. This is an internal/tooling surface (the
|
|
21
|
+
* rule-tester and the ESLint-plugin conformance harnesses) reached via the
|
|
22
|
+
* `@rslint/core/internal` subpath — the package root deliberately exposes only
|
|
23
|
+
* the high-level `Rslint` class as its linting surface, not this low-level engine.
|
|
24
|
+
*/
|
|
25
|
+
export declare function lint(options: LintOptions): Promise<LintResponse>;
|
|
26
|
+
|
|
27
|
+
export declare interface LintOptions {
|
|
28
|
+
files?: string[];
|
|
29
|
+
config?: Record<string, unknown>[];
|
|
30
|
+
configDirectory?: string;
|
|
31
|
+
workingDirectory?: string;
|
|
32
|
+
fileContents?: Record<string, string>;
|
|
33
|
+
includeEncodedSourceFiles?: boolean;
|
|
34
|
+
fix?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export declare interface LintResponse {
|
|
38
|
+
diagnostics: Diagnostic[];
|
|
39
|
+
errorCount: number;
|
|
40
|
+
warningCount: number;
|
|
41
|
+
fixableErrorCount: number;
|
|
42
|
+
fixableWarningCount: number;
|
|
43
|
+
fileCount: number;
|
|
44
|
+
ruleCount: number;
|
|
45
|
+
lintedFiles?: string[];
|
|
46
|
+
output?: Record<string, string>;
|
|
47
|
+
encodedSourceFiles?: Record<string, string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Node.js implementation of RslintService using child processes
|
|
52
|
+
*/
|
|
53
|
+
export declare class NodeRslintService implements RslintServiceInterface {
|
|
54
|
+
private nextMessageId;
|
|
55
|
+
private readonly pendingMessages;
|
|
56
|
+
private readonly rslintPath;
|
|
57
|
+
private readonly process;
|
|
58
|
+
private chunks;
|
|
59
|
+
private chunkSize;
|
|
60
|
+
private expectedSize;
|
|
61
|
+
private dead;
|
|
62
|
+
private closing;
|
|
63
|
+
constructor(options?: RSlintOptions);
|
|
64
|
+
/**
|
|
65
|
+
* Keep the Node event loop alive only while a request is in flight. The
|
|
66
|
+
* resident child and its stdio pipes are unref'd while idle so a caller that
|
|
67
|
+
* never calls close() still lets the process exit; they are ref'd around an
|
|
68
|
+
* in-flight request because a pending promise alone does NOT keep the loop
|
|
69
|
+
* alive — without the ref the loop could drain before the response arrives,
|
|
70
|
+
* leaving the await unsettled (Node would exit with code 13). The piped stdio
|
|
71
|
+
* streams are net.Socket at runtime (with ref/unref); child_process widens
|
|
72
|
+
* them to Readable/Writable, so narrow via `instanceof Socket` to reach those.
|
|
73
|
+
*/
|
|
74
|
+
private setLoopActive;
|
|
75
|
+
/**
|
|
76
|
+
* Send a message to the rslint process
|
|
77
|
+
*/
|
|
78
|
+
sendMessage(kind: string, data: any): Promise<any>;
|
|
79
|
+
/**
|
|
80
|
+
* Handle incoming binary data chunks
|
|
81
|
+
*/
|
|
82
|
+
private handleChunk;
|
|
83
|
+
/**
|
|
84
|
+
* Handle a complete message from rslint
|
|
85
|
+
*/
|
|
86
|
+
private handleMessage;
|
|
87
|
+
/**
|
|
88
|
+
* Reject every in-flight request and clear the queue. Called when the process
|
|
89
|
+
* dies (exit/error) or is terminated, so callers never hang on a process that
|
|
90
|
+
* can no longer reply. No-op when nothing is pending (the normal close path).
|
|
91
|
+
*/
|
|
92
|
+
private rejectAllPending;
|
|
93
|
+
/**
|
|
94
|
+
* Resolve every in-flight request (no payload) and clear the queue. Used on
|
|
95
|
+
* the expected close() shutdown path so the 'exit' request settles cleanly
|
|
96
|
+
* instead of rejecting. No-op when nothing is pending.
|
|
97
|
+
*/
|
|
98
|
+
private resolveAllPending;
|
|
99
|
+
/**
|
|
100
|
+
* Terminate the rslint process
|
|
101
|
+
*/
|
|
102
|
+
terminate(): void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Shared types for rslint IPC protocol across all environments
|
|
107
|
+
*/
|
|
108
|
+
declare interface Position {
|
|
109
|
+
line: number;
|
|
110
|
+
column: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare interface Range {
|
|
114
|
+
start: Position;
|
|
115
|
+
end: Position;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare interface RSlintOptions {
|
|
119
|
+
rslintPath?: string;
|
|
120
|
+
workingDirectory?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare interface RslintServiceInterface {
|
|
124
|
+
sendMessage(kind: string, data: any): Promise<any>;
|
|
125
|
+
terminate(): void;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
declare interface Suggestion {
|
|
129
|
+
messageId: string;
|
|
130
|
+
message: string;
|
|
131
|
+
data?: Record<string, string>;
|
|
132
|
+
fixes?: Fix[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { }
|