Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite8ef2a9

Browse files
authored
Supports ESLint v8. (#1317)
* Supports ESLint v8.* fix rule meta* update message
1 parent3aef00d commite8ef2a9

File tree

3 files changed

+113
-49
lines changed

3 files changed

+113
-49
lines changed

‎README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ This extension contributes the following variables to the [settings](https://cod
130130
}
131131
```
132132
-`eslint.packageManager`: controls the package manager to be used to resolve the ESLint library. This has only an influence if the ESLint library is resolved globally. Valid values are`"npm"` or`"yarn"` or`"pnpm"`.
133-
-`eslint.options`: options to configure how ESLint is started using the[ESLint CLI Engine API](http://eslint.org/docs/developer-guide/nodejs-api#cliengine). Defaults to an empty option bag.
133+
-`eslint.options`: options to configure how ESLint is started using the[ESLintclass API](http://eslint.org/docs/developer-guide/nodejs-api#eslint-class). (If you use ESLint<=v7, it will be used as an option for[CLI Engine](http://eslint.org/docs/developer-guide/nodejs-api#cliengine).) Defaults to an empty option bag.
134134
An example to point to a custom`.eslintrc.json` file is:
135135
```json
136136
{
137-
"eslint.options": {"configFile":"C:/mydirectory/.eslintrc.json" }
137+
"eslint.options": {"overrideConfigFile":"C:/mydirectory/.eslintrc.json" }
138138
}
139139
```
140140
-`eslint.run` - run the linter`onSave` or`onType`, default is`onType`.
@@ -145,7 +145,7 @@ This extension contributes the following variables to the [settings](https://cod
145145
-`eslint.probe` = an array for language identifiers for which the ESLint extension should be activated and should try to validate the file. If validation fails for probed languages the extension says silent. Defaults to`["javascript", "javascriptreact", "typescript", "typescriptreact", "html", "vue", "markdown"]`.
146146
-`eslint.validate` - an array of language identifiers specifying the files for which validation is to be enforced. This is an old legacy setting and should in normal cases not be necessary anymore. Defaults to`["javascript", "javascriptreact"]`.
147147
-`eslint.format.enable`: enables ESLint as a formatter for validated files. Although you can also use the formatter on save using the setting`editor.formatOnSave` it is recommended to use the`editor.codeActionsOnSave` feature since it allows for better configurability.
148-
-`eslint.workingDirectories` - specifies how the working directories ESLint is using are computed. ESLint resolves configuration files (e.g.`eslintrc`,`.eslintignore`) relative to a working directory so it is important to configure this correctly. If executing ESLint in the terminal requires you to change the working directory in the terminal into a sub folder then it is usually necessary to tweak this setting. (see also[CLIEngineoptions#cwd](https://eslint.org/docs/developer-guide/nodejs-api#cliengine)). Please also keep in mind that the`.eslintrc*` file is resolved considering the parent directories whereas the`.eslintignore` file is only honored in the current working directory. The following values can be used:
148+
-`eslint.workingDirectories` - specifies how the working directories ESLint is using are computed. ESLint resolves configuration files (e.g.`eslintrc`,`.eslintignore`) relative to a working directory so it is important to configure this correctly. If executing ESLint in the terminal requires you to change the working directory in the terminal into a sub folder then it is usually necessary to tweak this setting. (see also[ESLint classoptions#cwd](https://eslint.org/docs/developer-guide/nodejs-api#eslint-class)). Please also keep in mind that the`.eslintrc*` file is resolved considering the parent directories whereas the`.eslintignore` file is only honored in the current working directory. The following values can be used:
149149
-`[{ "mode": "location" }]` (@since 2.0.0): instructs ESLint to uses the workspace folder location or the file location (if no workspace folder is open) as the working directory. This is the default and is the same strategy as used in older versions of the ESLint extension (1.9.x versions).
150150
-`[{ "mode": "auto" }]` (@since 2.0.0): instructs ESLint to infer a working directory based on the location of`package.json`,`.eslintignore` and`.eslintrc*` files. This might work in many cases but can lead to unexpected results as well.
151151
-`string[]`: an array of working directories to use.

‎package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
"scope":"resource",
9393
"type":"object",
9494
"default": {},
95-
"markdownDescription":"The eslint options object to provide args normally passed to eslint when executed from a command line (see https://eslint.org/docs/developer-guide/nodejs-api#cliengine)."
95+
"markdownDescription":"The eslint options object to provide args normally passed to eslint when executed from a command line (see https://eslint.org/docs/developer-guide/nodejs-api#eslint-class)."
9696
},
9797
"eslint.trace.server": {
9898
"scope":"window",
@@ -250,7 +250,7 @@
250250
}
251251
]
252252
},
253-
"markdownDescription":"Specifies how the working directories ESLint is using are computed. ESLint resolves configuration files (e.g. `eslintrc`, `.eslintignore`) relative to a working directory so it is important to configure this correctly."
253+
"markdownDescription":"Specifies how the working directories ESLint is using are computed. ESLint resolves configuration files (e.g. `eslintrc`, `.eslintignore`) relative to a working directory so it is important to configure this correctly."
254254
},
255255
"eslint.validate": {
256256
"scope":"resource",
@@ -353,7 +353,7 @@
353353
}
354354
},
355355
"additionalProperties":false,
356-
"markdownDescription":"Show disable lint rule in the quick fix menu."
356+
"markdownDescription":"Show disable lint rule in the quick fix menu."
357357
},
358358
"eslint.codeAction.showDocumentation": {
359359
"scope":"resource",
@@ -369,7 +369,7 @@
369369
}
370370
},
371371
"additionalProperties":false,
372-
"markdownDescription":"Show open lint rule documentation web page in the quick fix menu."
372+
"markdownDescription":"Show open lint rule documentation web page in the quick fix menu."
373373
},
374374
"eslint.codeActionsOnSave.mode": {
375375
"scope":"resource",

‎server/src/eslintServer.ts

Lines changed: 106 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ interface CLIOptions {
286286
fix?:boolean;
287287
}
288288

289+
interfaceESLintClassOptions{
290+
cwd?:string;
291+
fixTypes?:string[];
292+
fix?:boolean;
293+
}
294+
289295
// { meta: { docs: [Object], schema: [Array] }, create: [Function: create]}
290296
interfaceRuleData{
291297
meta?:{
@@ -297,8 +303,8 @@ interface RuleData {
297303
}
298304

299305
namespaceRuleData{
300-
exportfunctionhasMetaType(value:RuleData|undefined):value isRuleData&{meta:{type:string;};}{
301-
returnvalue!==undefined&&value.meta!==undefined&&value.meta.type!==undefined;
306+
exportfunctionhasMetaType(value:RuleData['meta']|undefined):value isRuleData['meta']&{type:string;}{
307+
returnvalue!==undefined&&value.type!==undefined;
302308
}
303309
}
304310

@@ -323,6 +329,28 @@ interface ESLintConfig {
323329
settings:object;
324330
}
325331

332+
namespaceESLintClass{
333+
exportfunctionnewESLintClass(library:ESLintModule,newOptions:ESLintClassOptions|CLIOptions):ESLintClass{
334+
if(library.CLIEngine===undefined){
335+
returnnewlibrary.ESLint(newOptions);
336+
}else{
337+
constcli=newlibrary.CLIEngine(newOptions);
338+
returnnewESLintClassEmulator(cli);
339+
}
340+
}
341+
}
342+
343+
interfaceESLintClass{
344+
// https://eslint.org/docs/developer-guide/nodejs-api#-eslintlinttextcode-options
345+
lintText(content:string,options:{filePath?:string,warnIgnored?:boolean}):Promise<ESLintDocumentReport[]>;
346+
// https://eslint.org/docs/developer-guide/nodejs-api#-eslintispathignoredfilepath
347+
isPathIgnored(path:string):Promise<boolean>;
348+
// https://eslint.org/docs/developer-guide/nodejs-api#-eslintgetrulesmetaforresultsresults
349+
getRulesMetaForResults(results:ESLintDocumentReport[]):Record<string,RuleData['meta']>|undefined/* for ESLintClassEmulator */;
350+
// https://eslint.org/docs/developer-guide/nodejs-api#-eslintcalculateconfigforfilefilepath
351+
calculateConfigForFile(path:string):Promise<ESLintConfig|undefined/* for ESLintClassEmulator */>;
352+
}
353+
326354
interfaceCLIEngine{
327355
executeOnText(content:string,file?:string,warn?:boolean):ESLintReport;
328356
isPathIgnored(path:string):boolean;
@@ -337,13 +365,21 @@ namespace CLIEngine {
337365
}
338366
}
339367

368+
interfaceESLintClassConstructor{
369+
new(options:ESLintClassOptions):ESLintClass;
370+
}
371+
340372
interfaceCLIEngineConstructor{
341373
new(options:CLIOptions):CLIEngine;
342374
}
343375

344-
interfaceESLintModule{
376+
typeESLintModule={
345377
CLIEngine:CLIEngineConstructor;
346-
}
378+
}|{
379+
// for ESLint >= v8
380+
ESLint:ESLintClassConstructor;
381+
CLIEngine:undefined;
382+
};
347383

348384
declareconst__webpack_require__:typeofrequire;
349385
declareconst__non_webpack_require__:typeofrequire;
@@ -890,7 +926,7 @@ function resolveSettings(document: TextDocument): Promise<TextDocumentSettings>
890926
}
891927

892928
settings.silent=settings.validate===Validate.probe;
893-
returnpromise.then((libraryPath)=>{
929+
returnpromise.then(async(libraryPath)=>{
894930
letlibrary=path2Library.get(libraryPath);
895931
if(library===undefined){
896932
library=loadNodeModule(libraryPath);
@@ -899,9 +935,9 @@ function resolveSettings(document: TextDocument): Promise<TextDocumentSettings>
899935
if(!settings.silent){
900936
connection.console.error(`Failed to load eslint library from${libraryPath}. See output panel for more information.`);
901937
}
902-
}elseif(library.CLIEngine===undefined){
938+
}elseif(library.CLIEngine===undefined&&library.ESLint===undefined){
903939
settings.validate=Validate.off;
904-
connection.console.error(`The eslint library loaded from${libraryPath} doesn\'texporta CLIEngine. You need at least eslint@1.0.0`);
940+
connection.console.error(`The eslint library loaded from${libraryPath} doesn\'tneither exportsa CLIEngine nor an ESLint class. You need at least eslint@1.0.0`);
905941
}else{
906942
connection.console.info(`ESLint library loaded from:${libraryPath}`);
907943
settings.library=library;
@@ -928,13 +964,9 @@ function resolveSettings(document: TextDocument): Promise<TextDocumentSettings>
928964
if(defaultLanguageIds.has(document.languageId)){
929965
settings.validate=Validate.on;
930966
}elseif(parserRegExps!==undefined||pluginName!==undefined||parserOptions!==undefined){
931-
consteslintConfig:ESLintConfig|undefined=withCLIEngine((cli)=>{
967+
consteslintConfig:ESLintConfig|undefined=awaitwithESLintClass((eslintClass)=>{
932968
try{
933-
if(typeofcli.getConfigForFile==='function'){
934-
returncli.getConfigForFile(filePath!);
935-
}else{
936-
returnundefined;
937-
}
969+
returneslintClass.calculateConfigForFile(filePath!);
938970
}catch(err){
939971
returnundefined;
940972
}
@@ -995,8 +1027,8 @@ function resolveSettings(document: TextDocument): Promise<TextDocumentSettings>
9951027
formatterRegistrations.set(uri,connection.client.register(DocumentFormattingRequest.type,options));
9961028
}else{
9971029
constfilePath=getFilePath(uri)!;
998-
withCLIEngine((cli)=>{
999-
if(!cli.isPathIgnored(filePath)){
1030+
awaitwithESLintClass(async(eslintClass)=>{
1031+
if(!awaiteslintClass.isPathIgnored(filePath)){
10001032
formatterRegistrations.set(uri,connection.client.register(DocumentFormattingRequest.type,options));
10011033
}
10021034
},settings);
@@ -1304,12 +1336,12 @@ function validateSingle(document: TextDocument, publishDiagnostics: boolean = tr
13041336
if(!documents.get(document.uri)){
13051337
returnPromise.resolve(undefined);
13061338
}
1307-
returnresolveSettings(document).then((settings)=>{
1339+
returnresolveSettings(document).then(async(settings)=>{
13081340
if(settings.validate!==Validate.on||!TextDocumentSettings.hasLibrary(settings)){
13091341
return;
13101342
}
13111343
try{
1312-
validate(document,settings,publishDiagnostics);
1344+
awaitvalidate(document,settings,publishDiagnostics);
13131345
connection.sendNotification(StatusNotification.type,{uri:document.uri,state:Status.ok});
13141346
}catch(err){
13151347
// if an exception has occurred while validating clear all errors to ensure
@@ -1362,7 +1394,7 @@ const ruleDocData: {
13621394
};
13631395

13641396
constvalidFixTypes=newSet<string>(['problem','suggestion','layout']);
1365-
functionvalidate(document:TextDocument,settings:TextDocumentSettings&{library:ESLintModule},publishDiagnostics:boolean=true):void{
1397+
asyncfunctionvalidate(document:TextDocument,settings:TextDocumentSettings&{library:ESLintModule},publishDiagnostics:boolean=true):Promise<void>{
13661398
constnewOptions:CLIOptions=Object.assign(Object.create(null),settings.options);
13671399
letfixTypes:Set<string>|undefined=undefined;
13681400
if(Array.isArray(newOptions.fixTypes)&&newOptions.fixTypes.length>0){
@@ -1387,20 +1419,21 @@ function validate(document: TextDocument, settings: TextDocumentSettings & { lib
13871419
consturi=document.uri;
13881420
constfile=getFilePath(document);
13891421

1390-
withCLIEngine((cli)=>{
1422+
awaitwithESLintClass(async(eslintClass)=>{
13911423
codeActions.delete(uri);
1392-
constreport:ESLintReport=cli.executeOnText(content,file,settings.onIgnoredFiles!==ESLintSeverity.off);
1393-
if(CLIEngine.hasRule(cli)&&!ruleDocData.handled.has(uri)){
1424+
constreportResults:ESLintDocumentReport[]=awaiteslintClass.lintText(content,{filePath:file,warnIgnored:settings.onIgnoredFiles!==ESLintSeverity.off});
1425+
construlesMeta=eslintClass.getRulesMetaForResults(reportResults);
1426+
if(rulesMeta&&!ruleDocData.handled.has(uri)){
13941427
ruleDocData.handled.add(uri);
1395-
cli.getRules().forEach((rule,key)=>{
1396-
if(rule.meta&&rule.meta.docs&&Is.string(rule.meta.docs.url)){
1397-
ruleDocData.urls.set(key,rule.meta.docs.url);
1428+
Object.entries(rulesMeta).forEach(([key,meta])=>{
1429+
if(meta&&meta.docs&&Is.string(meta.docs.url)){
1430+
ruleDocData.urls.set(key,meta.docs.url);
13981431
}
13991432
});
14001433
}
14011434
constdiagnostics:Diagnostic[]=[];
1402-
if(report&&report.results&&Array.isArray(report.results)&&report.results.length>0){
1403-
constdocReport=report.results[0];
1435+
if(reportResults&&Array.isArray(reportResults)&&reportResults.length>0){
1436+
constdocReport=reportResults[0];
14041437
if(docReport.messages&&Array.isArray(docReport.messages)){
14051438
docReport.messages.forEach((problem)=>{
14061439
if(problem){
@@ -1416,9 +1449,9 @@ function validate(document: TextDocument, settings: TextDocumentSettings & { lib
14161449
}
14171450
constdiagnostic=makeDiagnostic(settings,problem);
14181451
diagnostics.push(diagnostic);
1419-
if(fixTypes!==undefined&&CLIEngine.hasRule(cli)&&problem.ruleId!==undefined&&problem.fix!==undefined){
1420-
construle=cli.getRules().get(problem.ruleId);
1421-
if(RuleData.hasMetaType(rule)&&fixTypes.has(rule.meta.type)){
1452+
if(fixTypes!==undefined&&rulesMeta&&problem.ruleId!==undefined&&problem.fix!==undefined){
1453+
constmeta=rulesMeta[problem.ruleId];
1454+
if(RuleData.hasMetaType(meta)&&fixTypes.has(meta.type)){
14221455
recordCodeAction(document,diagnostic,problem);
14231456
}
14241457
}else{
@@ -1434,8 +1467,8 @@ function validate(document: TextDocument, settings: TextDocumentSettings & { lib
14341467
},settings);
14351468
}
14361469

1437-
functionwithCLIEngine<T>(func:(cli:CLIEngine)=>T,settings:TextDocumentSettings&{library:ESLintModule},options?:CLIOptions):T{
1438-
constnewOptions:CLIOptions=options===undefined
1470+
functionwithESLintClass<T>(func:(eslintClass:ESLintClass)=>T,settings:TextDocumentSettings&{library:ESLintModule},options?:ESLintClassOptions|CLIOptions):T{
1471+
constnewOptions:ESLintClassOptions|CLIOptions=options===undefined
14391472
?Object.assign(Object.create(null),settings.options)
14401473
:Object.assign(Object.create(null),settings.options,options);
14411474

@@ -1447,15 +1480,46 @@ function withCLIEngine<T>(func: (cli: CLIEngine) => T, settings: TextDocumentSet
14471480
process.chdir(settings.workingDirectory.directory);
14481481
}
14491482
}
1450-
constcli=newsettings.library.CLIEngine(newOptions);
1451-
returnfunc(cli);
1483+
1484+
consteslintClass=ESLintClass.newESLintClass(settings.library,newOptions);
1485+
returnfunc(eslintClass);
14521486
}finally{
14531487
if(cwd!==process.cwd()){
14541488
process.chdir(cwd);
14551489
}
14561490
}
14571491
}
14581492

1493+
/**
1494+
* ESLint class emulator using CLI Engine.
1495+
*/
1496+
classESLintClassEmulatorimplementsESLintClass{
1497+
privatecli:CLIEngine;
1498+
1499+
constructor(cli:CLIEngine){
1500+
this.cli=cli;
1501+
}
1502+
asynclintText(content:string,options:{filePath?:string|undefined;warnIgnored?:boolean|undefined;}):Promise<ESLintDocumentReport[]>{
1503+
returnthis.cli.executeOnText(content,options.filePath,options.warnIgnored).results;
1504+
}
1505+
asyncisPathIgnored(path:string):Promise<boolean>{
1506+
returnthis.cli.isPathIgnored(path);
1507+
}
1508+
getRulesMetaForResults(_results:ESLintDocumentReport[]):Record<string,RuleData['meta']>|undefined{
1509+
if(!CLIEngine.hasRule(this.cli)){
1510+
returnundefined;
1511+
}
1512+
construles:Record<string,RuleData['meta']>={};
1513+
for(const[name,rule]ofthis.cli.getRules()){
1514+
rules[name]=rule.meta;
1515+
}
1516+
returnrules;
1517+
}
1518+
asynccalculateConfigForFile(path:string):Promise<ESLintConfig|undefined>{
1519+
returntypeofthis.cli.getConfigForFile==='function' ?this.cli.getConfigForFile(path) :undefined;
1520+
}
1521+
}
1522+
14591523
constnoConfigReported:Map<string,ESLintModule>=newMap<string,ESLintModule>();
14601524

14611525
functionisNoConfigFoundError(error:any):boolean{
@@ -1572,15 +1636,15 @@ function showErrorMessage(error: any, document: TextDocument): Status {
15721636
returnStatus.error;
15731637
}
15741638

1575-
messageQueue.registerNotification(DidChangeWatchedFilesNotification.type,(params)=>{
1639+
messageQueue.registerNotification(DidChangeWatchedFilesNotification.type,async(params)=>{
15761640
// A .eslintrc has change. No smartness here.
15771641
// Simply revalidate all file.
15781642
ruleDocData.handled.clear();
15791643
ruleDocData.urls.clear();
15801644
noConfigReported.clear();
15811645
missingModuleReported.clear();
15821646
document2Settings.clear();// config files can change plugins and parser.
1583-
params.changes.forEach((change)=>{
1647+
awaitPromise.all(params.changes.map(async(change)=>{
15841648
constfsPath=getFilePath(change.uri);
15851649
if(fsPath===undefined||fsPath.length===0||isUNC(fsPath)){
15861650
return;
@@ -1589,15 +1653,15 @@ messageQueue.registerNotification(DidChangeWatchedFilesNotification.type, (param
15891653
if(dirname){
15901654
constlibrary=configErrorReported.get(fsPath);
15911655
if(library!==undefined){
1592-
constcli=newlibrary.CLIEngine({});
1656+
consteslintClass=ESLintClass.newESLintClass(library,{});
15931657
try{
1594-
cli.executeOnText('',path.join(dirname,'___test___.js'));
1658+
awaiteslintClass.lintText('',{filePath:path.join(dirname,'___test___.js')});
15951659
configErrorReported.delete(fsPath);
15961660
}catch(error){
15971661
}
15981662
}
15991663
}
1600-
});
1664+
}));
16011665
validateMany(documents.all());
16021666
});
16031667

@@ -2043,7 +2107,7 @@ function computeAllFixes(identifier: VersionedTextDocumentIdentifier, mode: AllF
20432107
return[];
20442108
}
20452109
constfilePath=getFilePath(textDocument);
2046-
returnwithCLIEngine((cli)=>{
2110+
returnwithESLintClass(async(eslintClass)=>{
20472111
constproblems=codeActions.get(uri);
20482112
constoriginalContent=textDocument.getText();
20492113
letproblemFixes:TextEdit[]|undefined;
@@ -2069,10 +2133,10 @@ function computeAllFixes(identifier: VersionedTextDocumentIdentifier, mode: AllF
20692133
}else{
20702134
content=originalContent;
20712135
}
2072-
constreport=cli.executeOnText(content,filePath);
2136+
constreportResults=awaiteslintClass.lintText(content,{filePath});
20732137
connection.tracer.log(`Computing all fixes took:${Date.now()-start} ms.`);
2074-
if(Array.isArray(report.results)&&report.results.length===1&&report.results[0].output!==undefined){
2075-
constfixedContent=report.results[0].output;
2138+
if(Array.isArray(reportResults)&&reportResults.length===1&&reportResults[0].output!==undefined){
2139+
constfixedContent=reportResults[0].output;
20762140
start=Date.now();
20772141
constdiffs=stringDiff(originalContent,fixedContent,false);
20782142
connection.tracer.log(`Computing minimal edits took:${Date.now()-start} ms.`);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp