vscode-eslint 0.0.2 → 0.0.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.
Files changed (181) hide show
  1. package/bin/vscode-eslint +82 -0
  2. package/client/out/client.d.ts +13 -0
  3. package/client/out/extension.d.ts +3 -0
  4. package/client/out/extension.js +2 -0
  5. package/client/out/extension.js.map +1 -0
  6. package/client/out/node-utils.d.ts +51 -0
  7. package/client/out/settings.d.ts +41 -0
  8. package/client/out/shared/customMessages.d.ts +89 -0
  9. package/client/out/shared/settings.d.ts +106 -0
  10. package/client/out/tasks.d.ts +31 -0
  11. package/client/out/tests/glob.test.d.ts +1 -0
  12. package/client/out/vscode-utils.d.ts +5 -0
  13. package/package.json +9 -2
  14. package/server/out/diff.d.ts +134 -0
  15. package/server/out/eslint.d.ts +298 -0
  16. package/server/out/eslintServer.d.ts +1 -0
  17. package/server/out/eslintServer.js +2 -0
  18. package/server/out/eslintServer.js.map +1 -0
  19. package/server/out/is.d.ts +3 -0
  20. package/server/out/languageDefaults.d.ts +6 -0
  21. package/server/out/linkedMap.d.ts +53 -0
  22. package/server/out/paths.d.ts +22 -0
  23. package/server/out/shared/customMessages.d.ts +89 -0
  24. package/server/out/shared/settings.d.ts +106 -0
  25. package/$shared/customMessages.ts +0 -113
  26. package/$shared/settings.ts +0 -189
  27. package/.CodeQL.yml +0 -5
  28. package/.azure-pipelines.yml +0 -27
  29. package/.gemini/agents/vscode-eslint.md +0 -23
  30. package/.github/commands.yml +0 -127
  31. package/.github/locker.yml +0 -6
  32. package/.github/needs_more_info.yml +0 -6
  33. package/.github/workflows/npm-publish.yml +0 -53
  34. package/.github/workflows/release-please.yml +0 -22
  35. package/.lsifrc.json +0 -4
  36. package/.vscode/launch.json +0 -20
  37. package/.vscode/settings.json +0 -52
  38. package/.vscode/spellright.dict +0 -8
  39. package/.vscode/tasks.json +0 -39
  40. package/.vscodeignore +0 -23
  41. package/CHANGELOG.md +0 -524
  42. package/SECURITY.md +0 -41
  43. package/agents.md +0 -36
  44. package/bin/vscode-eslint.js +0 -56
  45. package/build/azure-pipelines/linux/build.yml +0 -14
  46. package/build/azure-pipelines/pre-release.yml +0 -42
  47. package/build/azure-pipelines/release.yml +0 -45
  48. package/build/azure-pipelines/win32/build.yml +0 -14
  49. package/build/bin/all.js +0 -29
  50. package/build/bin/linking.js +0 -102
  51. package/build/bin/symlink.js +0 -35
  52. package/client/.mocharc.json +0 -6
  53. package/client/agents.md +0 -5
  54. package/client/package-lock.json +0 -176
  55. package/client/package.json +0 -29
  56. package/client/src/client.ts +0 -992
  57. package/client/src/extension.ts +0 -180
  58. package/client/src/node-utils.ts +0 -393
  59. package/client/src/settings.ts +0 -379
  60. package/client/src/tasks.ts +0 -186
  61. package/client/src/tests/glob.test.ts +0 -31
  62. package/client/src/vscode-utils.ts +0 -28
  63. package/client/test/mocha.opts +0 -3
  64. package/client/tsconfig.json +0 -20
  65. package/client/webpack.config.js +0 -25
  66. package/contributing.md +0 -19
  67. package/esbuild.js +0 -62
  68. package/eslint.config.js +0 -129
  69. package/history/settings_1_9_x.md +0 -110
  70. package/images/2_1_10/eslint-dialog.png +0 -0
  71. package/images/2_1_10/eslint-status.png +0 -0
  72. package/package-json-schema.json +0 -9
  73. package/playgrounds/7.0/.eslintignore +0 -1
  74. package/playgrounds/7.0/.eslintrc.json +0 -71
  75. package/playgrounds/7.0/.vscode/settings.json +0 -85
  76. package/playgrounds/7.0/app.js +0 -12
  77. package/playgrounds/7.0/build/.eslintignore +0 -1
  78. package/playgrounds/7.0/build/.eslintrc.json +0 -30
  79. package/playgrounds/7.0/build/build.js +0 -11
  80. package/playgrounds/7.0/jsconfig.json +0 -5
  81. package/playgrounds/7.0/package-lock.json +0 -2133
  82. package/playgrounds/7.0/package.json +0 -10
  83. package/playgrounds/7.0/readme.md +0 -0
  84. package/playgrounds/7.0/subDir/sub.js +0 -11
  85. package/playgrounds/7.0/subDir/test.jsx +0 -10
  86. package/playgrounds/7.0/test.js +0 -11
  87. package/playgrounds/7.0/test.sh +0 -1
  88. package/playgrounds/7.0/test.vue +0 -33
  89. package/playgrounds/7.0/test2.html +0 -8
  90. package/playgrounds/8.0/.eslintignore +0 -1
  91. package/playgrounds/8.0/.eslintrc.json +0 -71
  92. package/playgrounds/8.0/.vscode/settings.json +0 -91
  93. package/playgrounds/8.0/app.js +0 -12
  94. package/playgrounds/8.0/build/.eslintignore +0 -1
  95. package/playgrounds/8.0/build/.eslintrc.json +0 -30
  96. package/playgrounds/8.0/build/build.js +0 -11
  97. package/playgrounds/8.0/jsconfig.json +0 -5
  98. package/playgrounds/8.0/package-lock.json +0 -2321
  99. package/playgrounds/8.0/package.json +0 -10
  100. package/playgrounds/8.0/readme.md +0 -17
  101. package/playgrounds/8.0/subDir/sub.js +0 -11
  102. package/playgrounds/8.0/subDir/test.jsx +0 -10
  103. package/playgrounds/8.0/test.ipynb +0 -49
  104. package/playgrounds/8.0/test.js +0 -3
  105. package/playgrounds/8.0/test.sh +0 -1
  106. package/playgrounds/8.0/test.vue +0 -33
  107. package/playgrounds/8.0/test2.html +0 -8
  108. package/playgrounds/9.0/flat/.vscode/settings.json +0 -3
  109. package/playgrounds/9.0/flat/app.js +0 -12
  110. package/playgrounds/9.0/flat/dist/ignore.js +0 -12
  111. package/playgrounds/9.0/flat/eslint.config.js +0 -61
  112. package/playgrounds/9.0/flat/package-lock.json +0 -1053
  113. package/playgrounds/9.0/flat/package.json +0 -9
  114. package/playgrounds/9.0/rc/.eslintrc.json +0 -57
  115. package/playgrounds/9.0/rc/.vscode/settings.json +0 -3
  116. package/playgrounds/9.0/rc/app.js +0 -12
  117. package/playgrounds/9.0/rc/package-lock.json +0 -1345
  118. package/playgrounds/9.0/rc/package.json +0 -9
  119. package/playgrounds/flat-config/.vscode/settings.json +0 -22
  120. package/playgrounds/flat-config/app.js +0 -12
  121. package/playgrounds/flat-config/eslint.config.js +0 -51
  122. package/playgrounds/flat-config/package-lock.json +0 -2733
  123. package/playgrounds/flat-config/package.json +0 -12
  124. package/playgrounds/flat-config/sub/sub.js +0 -2
  125. package/playgrounds/flat-config/test.ts +0 -7
  126. package/playgrounds/flat-config/tsconfig.json +0 -11
  127. package/playgrounds/flat-config-fail/f1/app.js +0 -12
  128. package/playgrounds/flat-config-fail/f1/eslint.config.js +0 -51
  129. package/playgrounds/flat-config-fail/package-lock.json +0 -1683
  130. package/playgrounds/flat-config-fail/package.json +0 -11
  131. package/playgrounds/flat-config-mjs/.vscode/settings.json +0 -21
  132. package/playgrounds/flat-config-mjs/app.js +0 -12
  133. package/playgrounds/flat-config-mjs/eslint.config.mjs +0 -53
  134. package/playgrounds/flat-config-mjs/package-lock.json +0 -2860
  135. package/playgrounds/flat-config-mjs/package.json +0 -11
  136. package/playgrounds/flat-config-mjs/sub/sub.js +0 -2
  137. package/playgrounds/flat-config-mjs/test.ts +0 -7
  138. package/playgrounds/flat-config-mjs/tsconfig.json +0 -11
  139. package/playgrounds/load-eslint/.vscode/settings.json +0 -21
  140. package/playgrounds/load-eslint/app.js +0 -12
  141. package/playgrounds/load-eslint/eslint.config.js +0 -51
  142. package/playgrounds/load-eslint/package-lock.json +0 -2860
  143. package/playgrounds/load-eslint/package.json +0 -11
  144. package/playgrounds/load-eslint/sub/sub.js +0 -2
  145. package/playgrounds/load-eslint/test.ts +0 -7
  146. package/playgrounds/load-eslint/tsconfig.json +0 -11
  147. package/playgrounds/noLib/test.js +0 -22
  148. package/playgrounds/noWD/.vscode/settings.json +0 -2
  149. package/playgrounds/noWD/src/.eslintrc.json +0 -18
  150. package/playgrounds/noWD/src/package-lock.json +0 -2812
  151. package/playgrounds/noWD/src/package.json +0 -12
  152. package/playgrounds/noWD/src/test.js +0 -3
  153. package/playgrounds/notebooks/notebook.ipynb +0 -7072
  154. package/playgrounds/notebooks/notebook2.ipynb +0 -20
  155. package/playgrounds/testing.code-workspace +0 -28
  156. package/playgrounds/ts/.eslintrc.base.json +0 -23
  157. package/playgrounds/ts/.eslintrc.json +0 -191
  158. package/playgrounds/ts/.vscode/settings.json +0 -12
  159. package/playgrounds/ts/package-lock.json +0 -2687
  160. package/playgrounds/ts/package.json +0 -11
  161. package/playgrounds/ts/test copy.ts +0 -4
  162. package/playgrounds/ts/test.ipynb +0 -49
  163. package/playgrounds/ts/test.ts +0 -4
  164. package/playgrounds/ts/test.tsx +0 -14
  165. package/playgrounds/ts/tsconfig.json +0 -100
  166. package/server/agents.md +0 -9
  167. package/server/package-lock.json +0 -93
  168. package/server/package.json +0 -32
  169. package/server/src/diff.ts +0 -1079
  170. package/server/src/eslint.ts +0 -1471
  171. package/server/src/eslintServer.ts +0 -865
  172. package/server/src/is.ts +0 -18
  173. package/server/src/languageDefaults.ts +0 -40
  174. package/server/src/linkedMap.ts +0 -448
  175. package/server/src/paths.ts +0 -128
  176. package/server/src/thenable.d.ts +0 -5
  177. package/server/tsconfig.json +0 -21
  178. package/server/webpack.config.js +0 -25
  179. package/shared.webpack.config.js +0 -59
  180. package/tsconfig.base.json +0 -9
  181. package/tsconfig.json +0 -21
@@ -1,1471 +0,0 @@
1
- /* --------------------------------------------------------------------------------------------
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License. See License.txt in the project root for license information.
4
- * ------------------------------------------------------------------------------------------ */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import * as crypto from 'crypto';
8
- import { execSync } from 'child_process';
9
-
10
- import semverParse = require('semver/functions/parse');
11
- import semverGte = require('semver/functions/gte');
12
-
13
- import { TextDocument } from 'vscode-languageserver-textdocument';
14
- import {
15
- Diagnostic, DiagnosticSeverity, DiagnosticTag, ProposedFeatures, Range, TextEdit, Files, DocumentFilter, DocumentFormattingRegistrationOptions,
16
- Disposable, DocumentFormattingRequest, TextDocuments, uinteger
17
- } from 'vscode-languageserver/node';
18
- import { URI } from 'vscode-uri';
19
-
20
- import { ProbeFailedParams, ProbeFailedRequest, NoESLintLibraryRequest, Status, NoConfigRequest, StatusNotification } from './shared/customMessages';
21
- import { ConfigurationSettings, DirectoryItem, ESLintOptions, ESLintSeverity, ModeEnum, ModeItem, PackageManagers, RuleCustomization, RuleSeverity, Validate } from './shared/settings';
22
-
23
- import * as Is from './is';
24
- import { LRUCache } from './linkedMap';
25
- import { isUNC, normalizeDriveLetter, normalizePath } from './paths';
26
- import LanguageDefaults from './languageDefaults';
27
-
28
-
29
- /**
30
- * ESLint specific settings for a text document.
31
- */
32
- export type TextDocumentSettings = Omit<ConfigurationSettings, 'workingDirectory'> & {
33
- silent: boolean;
34
- workingDirectory: DirectoryItem | undefined;
35
- library: ESLintModule | undefined;
36
- resolvedGlobalPackageManagerPath: string | undefined;
37
- };
38
-
39
- export namespace TextDocumentSettings {
40
- export function hasLibrary(settings: TextDocumentSettings): settings is (TextDocumentSettings & { library: ESLintModule }) {
41
- return settings.library !== undefined;
42
- }
43
- }
44
-
45
- /**
46
- * A special error thrown by the ESLint library
47
- */
48
- export interface ESLintError extends Error {
49
- messageTemplate?: string;
50
- messageData?: {
51
- pluginName?: string;
52
- };
53
- }
54
-
55
- export namespace ESLintError {
56
- export function isNoConfigFound(error: any): boolean {
57
- const candidate = error as ESLintError;
58
- return candidate.messageTemplate === 'no-config-found' || candidate.message === 'No ESLint configuration found.';
59
- }
60
- }
61
-
62
- type ESLintAutoFixEdit = {
63
- range: [number, number];
64
- text: string;
65
- };
66
-
67
- type ESLintSuggestionResult = {
68
- desc: string;
69
- fix: ESLintAutoFixEdit;
70
- };
71
-
72
- type ESLintProblem = {
73
- line: number;
74
- column: number;
75
- endLine?: number;
76
- endColumn?: number;
77
- severity: number;
78
- ruleId: string;
79
- message: string;
80
- fix?: ESLintAutoFixEdit;
81
- suggestions?: ESLintSuggestionResult[];
82
- };
83
-
84
- type ESLintDocumentReport = {
85
- filePath: string;
86
- errorCount: number;
87
- warningCount: number;
88
- messages: ESLintProblem[];
89
- output?: string;
90
- };
91
-
92
- type ESLintReport = {
93
- errorCount: number;
94
- warningCount: number;
95
- results: ESLintDocumentReport[];
96
- };
97
-
98
- export type CLIOptions = {
99
- cwd?: string;
100
- fixTypes?: string[];
101
- fix?: boolean;
102
- };
103
-
104
- export type SeverityConf = 0 | 1 | 2 | 'off' | 'warn' | 'error';
105
-
106
- export type RuleConf = SeverityConf | [SeverityConf, ...any[]];
107
-
108
- export type ConfigData = {
109
- rules?: Record<string, RuleConf>;
110
- };
111
-
112
- export type ESLintClassOptions = {
113
- cwd?: string;
114
- fixTypes?: string[];
115
- fix?: boolean;
116
- overrideConfig?: ConfigData;
117
- overrideConfigFile?: string | null;
118
- };
119
-
120
- export type RuleMetaData = {
121
- docs?: {
122
- url?: string;
123
- };
124
- type?: string;
125
- };
126
-
127
- export namespace RuleMetaData {
128
- // For unused eslint-disable comments, ESLint does not include a rule ID
129
- // nor any other metadata (although they do provide a fix). In order to
130
- // provide code actions for these, we create a fake rule ID and metadata.
131
- export const unusedDisableDirectiveId = 'unused-disable-directive';
132
- const unusedDisableDirectiveMeta: RuleMetaData = {
133
- docs: {
134
- url: 'https://eslint.org/docs/latest/use/configure/rules#report-unused-eslint-disable-comments'
135
- },
136
- type: 'directive'
137
- };
138
-
139
- const handled: Set<string> = new Set();
140
- const ruleId2Meta: Map<string, RuleMetaData> = new Map([[unusedDisableDirectiveId, unusedDisableDirectiveMeta]]);
141
-
142
- export function capture(eslint: ESLintClass, reports: ESLintDocumentReport[]): void {
143
- let rulesMetaData: Record<string, RuleMetaData> | undefined;
144
- if (eslint.isCLIEngine) {
145
- const toHandle = reports.filter(report => !handled.has(report.filePath));
146
- if (toHandle.length === 0) {
147
- return;
148
- }
149
- rulesMetaData = typeof eslint.getRulesMetaForResults === 'function' ? eslint.getRulesMetaForResults(toHandle) : undefined;
150
- toHandle.forEach(report => handled.add(report.filePath));
151
- } else {
152
- rulesMetaData = typeof eslint.getRulesMetaForResults === 'function' ? eslint.getRulesMetaForResults(reports) : undefined;
153
- }
154
- if (rulesMetaData === undefined) {
155
- return undefined;
156
- }
157
- Object.entries(rulesMetaData).forEach(([key, meta]) => {
158
- if (ruleId2Meta.has(key)) {
159
- return;
160
- }
161
- if (meta && meta.docs && Is.string(meta.docs.url)) {
162
- ruleId2Meta.set(key, meta);
163
- }
164
- });
165
- }
166
-
167
- export function clear(): void {
168
- handled.clear();
169
- ruleId2Meta.clear();
170
- ruleId2Meta.set(unusedDisableDirectiveId, unusedDisableDirectiveMeta);
171
- }
172
-
173
- export function getUrl(ruleId: string): string | undefined {
174
- return ruleId2Meta.get(ruleId)?.docs?.url;
175
- }
176
-
177
- export function getType(ruleId: string): string | undefined {
178
- return ruleId2Meta.get(ruleId)?.type;
179
- }
180
-
181
- export function hasRuleId(ruleId: string): boolean {
182
- return ruleId2Meta.has(ruleId);
183
- }
184
-
185
- export function isUnusedDisableDirectiveProblem(problem: ESLintProblem): boolean {
186
- return problem.ruleId === null && problem.message.startsWith('Unused eslint-disable directive');
187
- }
188
- }
189
-
190
- type ParserOptions = {
191
- parser?: string;
192
- };
193
-
194
- type ESLintRcConfig = {
195
- env: Record<string, boolean>;
196
- extends: string | string[];
197
- // globals: Record<string, GlobalConf>;
198
- ignorePatterns: string | string[];
199
- noInlineConfig: boolean;
200
- // overrides: OverrideConfigData[];
201
- parser: string | null;
202
- parserOptions?: ParserOptions;
203
- plugins: string[];
204
- processor: string;
205
- reportUnusedDisableDirectives: boolean | undefined;
206
- root: boolean;
207
- rules: Record<string, RuleConf>;
208
- settings: object;
209
- };
210
- type ESLintConfig = ESLintRcConfig;
211
-
212
- export type Problem = {
213
- label: string;
214
- documentVersion: number;
215
- ruleId: string;
216
- line: number;
217
- diagnostic: Diagnostic;
218
- edit?: ESLintAutoFixEdit;
219
- suggestions?: ESLintSuggestionResult[];
220
- };
221
-
222
- export namespace Problem {
223
- export function isFixable(problem: Problem): problem is FixableProblem {
224
- return problem.edit !== undefined;
225
- }
226
-
227
- export function hasSuggestions(problem: Problem): problem is SuggestionsProblem {
228
- return problem.suggestions !== undefined;
229
- }
230
- }
231
-
232
- export type FixableProblem = Problem & {
233
- edit: ESLintAutoFixEdit;
234
- };
235
-
236
- export namespace FixableProblem {
237
- export function createTextEdit(document: TextDocument, editInfo: FixableProblem): TextEdit {
238
- return TextEdit.replace(Range.create(document.positionAt(editInfo.edit.range[0]), document.positionAt(editInfo.edit.range[1])), editInfo.edit.text || '');
239
- }
240
- }
241
-
242
- export type SuggestionsProblem = Problem & {
243
- suggestions: ESLintSuggestionResult[];
244
- };
245
-
246
- export namespace SuggestionsProblem {
247
- export function createTextEdit(document: TextDocument, suggestion: ESLintSuggestionResult): TextEdit {
248
- return TextEdit.replace(Range.create(document.positionAt(suggestion.fix.range[0]), document.positionAt(suggestion.fix.range[1])), suggestion.fix.text || '');
249
- }
250
- }
251
-
252
- interface ESLintClass extends Object {
253
- // https://eslint.org/docs/developer-guide/nodejs-api#-eslintlinttextcode-options
254
- lintText(content: string, options: {filePath?: string; warnIgnored?: boolean}): Promise<ESLintDocumentReport[]>;
255
- // https://eslint.org/docs/developer-guide/nodejs-api#-eslintispathignoredfilepath
256
- isPathIgnored(path: string): Promise<boolean>;
257
- // https://eslint.org/docs/developer-guide/nodejs-api#-eslintgetrulesmetaforresultsresults
258
- getRulesMetaForResults?(results: ESLintDocumentReport[]): Record<string, RuleMetaData> | undefined /* for ESLintClassEmulator */;
259
- // https://eslint.org/docs/developer-guide/nodejs-api#-eslintcalculateconfigforfilefilepath
260
- calculateConfigForFile(path: string): Promise<ESLintConfig | undefined /* for ESLintClassEmulator */>;
261
- // Whether it is the old CLI Engine
262
- isCLIEngine?: boolean;
263
- }
264
-
265
- namespace ESLintClass {
266
- export function getConfigType(eslint: ESLintClass): 'eslintrc' | 'flat' {
267
- if (eslint.isCLIEngine === true) {
268
- return 'eslintrc';
269
- }
270
- const configType = (eslint.constructor as ESLintClassConstructor).configType;
271
- return configType ?? 'eslintrc';
272
- }
273
- }
274
-
275
- interface ESLintClassConstructor {
276
- configType?: 'eslintrc' | 'flat';
277
- version?: string;
278
- new(options: ESLintClassOptions): ESLintClass;
279
- }
280
-
281
- interface CLIEngineConstructor {
282
- new(options: CLIOptions): CLIEngine;
283
- }
284
-
285
- /**
286
- * A loaded ESLint npm module.
287
- */
288
- export type ESLintModule =
289
- {
290
- // version < 7.0
291
- ESLint: undefined;
292
- CLIEngine: CLIEngineConstructor;
293
- loadESLint?: undefined;
294
- } | {
295
- // 7.0 <= version < 8.0
296
- ESLint: ESLintClassConstructor;
297
- CLIEngine: CLIEngineConstructor;
298
- loadESLint?: undefined;
299
- } | {
300
- // 8.0 <= version.
301
- ESLint: ESLintClassConstructor;
302
- isFlatConfig?: boolean;
303
- CLIEngine: undefined;
304
- loadESLint?: (options?: { cwd?: string; useFlatConfig?: boolean }) => Promise<ESLintClassConstructor>;
305
- };
306
-
307
- export namespace ESLintModule {
308
- export function hasLoadESLint(value: ESLintModule): value is { ESLint: ESLintClassConstructor; CLIEngine: undefined; loadESLint: (options?: { cwd?: string; useFlatConfig?: boolean }) => Promise<ESLintClassConstructor> } {
309
- return value.loadESLint !== undefined;
310
- }
311
- export function hasESLintClass(value: ESLintModule): value is { ESLint: ESLintClassConstructor; CLIEngine: undefined } {
312
- return value.ESLint !== undefined;
313
- }
314
- export function hasCLIEngine(value: ESLintModule): value is { ESLint: undefined; CLIEngine: CLIEngineConstructor } {
315
- return value.CLIEngine !== undefined;
316
- }
317
- export function isFlatConfig(value: ESLintModule): value is { ESLint: ESLintClassConstructor; CLIEngine: undefined; isFlatConfig: true } {
318
- const candidate: { ESLint: ESLintClassConstructor; isFlatConfig?: boolean } = value as any;
319
- return candidate.ESLint !== undefined && candidate.isFlatConfig === true;
320
- }
321
- }
322
-
323
- // { meta: { docs: [Object], schema: [Array] }, create: [Function: create] }
324
- type RuleData = {
325
- meta?: RuleMetaData;
326
- };
327
-
328
- namespace RuleData {
329
- export function hasMetaType(value: RuleMetaData | undefined): value is RuleMetaData & { type: string } {
330
- return value !== undefined && value.type !== undefined;
331
- }
332
- }
333
-
334
- interface CLIEngine {
335
- executeOnText(content: string, file?: string, warn?: boolean): ESLintReport;
336
- isPathIgnored(path: string): boolean;
337
- // This is only available from v4.15.0 forward
338
- getRules?(): Map<string, RuleData>;
339
- getConfigForFile?(path: string): ESLintConfig;
340
- }
341
-
342
- namespace CLIEngine {
343
- export function hasRule(value: CLIEngine): value is CLIEngine & { getRules(): Map<string, RuleData> } {
344
- return value.getRules !== undefined;
345
- }
346
- }
347
-
348
- /**
349
- * ESLint class emulator using CLI Engine.
350
- */
351
- class ESLintClassEmulator implements ESLintClass {
352
-
353
- private cli: CLIEngine;
354
-
355
- constructor(cli: CLIEngine) {
356
- this.cli = cli;
357
- }
358
- get isCLIEngine(): boolean {
359
- return true;
360
- }
361
- async lintText(content: string, options: { filePath?: string | undefined; warnIgnored?: boolean | undefined }): Promise<ESLintDocumentReport[]> {
362
- return this.cli.executeOnText(content, options.filePath, options.warnIgnored).results;
363
- }
364
- async isPathIgnored(path: string): Promise<boolean> {
365
- return this.cli.isPathIgnored(path);
366
- }
367
- getRulesMetaForResults(_results: ESLintDocumentReport[]): Record<string, RuleMetaData> | undefined {
368
- if (!CLIEngine.hasRule(this.cli)) {
369
- return undefined;
370
- }
371
- const rules: Record<string, RuleMetaData> = {};
372
- for (const [name, rule] of this.cli.getRules()) {
373
- if (rule.meta !== undefined) {
374
- rules[name] = rule.meta;
375
- }
376
- }
377
- return rules;
378
- }
379
- async calculateConfigForFile(path: string): Promise<ESLintConfig | undefined> {
380
- return typeof this.cli.getConfigForFile === 'function' ? this.cli.getConfigForFile(path) : undefined;
381
- }
382
- }
383
-
384
-
385
- /**
386
- * Class for dealing with Fixes.
387
- */
388
- export class Fixes {
389
- constructor(private edits: Map<string, Problem>) {
390
- }
391
-
392
- public static overlaps(a: FixableProblem | undefined, b: FixableProblem): boolean {
393
- return a !== undefined && a.edit.range[1] > b.edit.range[0];
394
- }
395
-
396
- public static sameRange(a: FixableProblem, b: FixableProblem): boolean {
397
- return a.edit.range[0] === b.edit.range[0] && a.edit.range[1] === b.edit.range[1];
398
- }
399
-
400
- public isEmpty(): boolean {
401
- return this.edits.size === 0;
402
- }
403
-
404
- public getDocumentVersion(): number {
405
- if (this.isEmpty()) {
406
- throw new Error('No edits recorded.');
407
- }
408
- return this.edits.values().next().value!.documentVersion;
409
- }
410
-
411
- public getScoped(diagnostics: Diagnostic[]): Problem[] {
412
- const result: Problem[] = [];
413
- for (const diagnostic of diagnostics) {
414
- const key = Diagnostics.computeKey(diagnostic);
415
- const editInfo = this.edits.get(key);
416
- if (editInfo) {
417
- result.push(editInfo);
418
- }
419
- }
420
- return result;
421
- }
422
-
423
- public getAllSorted(): FixableProblem[] {
424
- const result: FixableProblem[] = [];
425
- for (const value of this.edits.values()) {
426
- if (Problem.isFixable(value)) {
427
- result.push(value);
428
- }
429
- }
430
- return result.sort((a, b) => {
431
- const d0 = a.edit.range[0] - b.edit.range[0];
432
- if (d0 !== 0) {
433
- return d0;
434
- }
435
- // Both edits have now the same start offset.
436
-
437
- // Length of a and length of b
438
- const al = a.edit.range[1] - a.edit.range[0];
439
- const bl = b.edit.range[1] - b.edit.range[0];
440
- // Both has the same start offset and length.
441
- if (al === bl) {
442
- return 0;
443
- }
444
-
445
- if (al === 0) {
446
- return -1;
447
- }
448
- if (bl === 0) {
449
- return 1;
450
- }
451
- return al - bl;
452
- });
453
- }
454
-
455
- public getApplicable(): FixableProblem[] {
456
- const sorted = this.getAllSorted();
457
- if (sorted.length <= 1) {
458
- return sorted;
459
- }
460
- const result: FixableProblem[] = [];
461
- let last: FixableProblem = sorted[0];
462
- result.push(last);
463
- for (let i = 1; i < sorted.length; i++) {
464
- const current = sorted[i];
465
- if (!Fixes.overlaps(last, current) && !Fixes.sameRange(last, current)) {
466
- result.push(current);
467
- last = current;
468
- }
469
- }
470
- return result;
471
- }
472
- }
473
-
474
- export type SaveRuleConfigItem = { offRules: Set<string>; onRules: Set<string>; options: ESLintOptions | undefined};
475
-
476
- /**
477
- * Manages the special save rule configurations done in the VS Code settings.
478
- */
479
- export namespace SaveRuleConfigs {
480
-
481
- export let inferFilePath: (documentOrUri: string | TextDocument | URI | undefined, useRealpaths: boolean) => string | undefined;
482
-
483
- const saveRuleConfigCache = new LRUCache<string, SaveRuleConfigItem | null>(128);
484
- export async function get(uri: string, settings: TextDocumentSettings & { library: ESLintModule }): Promise<SaveRuleConfigItem | undefined> {
485
- const filePath = inferFilePath(uri, settings.useRealpaths);
486
- let result = saveRuleConfigCache.get(uri);
487
- if (filePath === undefined || result === null) {
488
- return undefined;
489
- }
490
- if (result !== undefined) {
491
- return result;
492
- }
493
- const rules = settings.codeActionOnSave.rules;
494
- const options = settings.codeActionOnSave.options;
495
- result = await ESLint.withClass(async (eslint) => {
496
- if ((rules === undefined && options === undefined) || eslint.isCLIEngine) {
497
- return undefined;
498
- }
499
- const config = await eslint.calculateConfigForFile(filePath);
500
- if (config === undefined || config.rules === undefined || config.rules.length === 0) {
501
- return undefined;
502
- }
503
- const offRules: Set<string> = new Set();
504
- const onRules: Set<string> = new Set();
505
- if (rules !== undefined) {
506
- if (rules.length === 0) {
507
- Object.keys(config.rules).forEach(ruleId => offRules.add(ruleId));
508
- } else {
509
- for (const ruleId of Object.keys(config.rules)) {
510
- if (isOff(ruleId, rules)) {
511
- offRules.add(ruleId);
512
- } else {
513
- onRules.add(ruleId);
514
- }
515
- }
516
- }
517
- }
518
- return (offRules.size > 0 || options) ? { offRules, onRules, options } : undefined;
519
- }, settings);
520
- if (result === undefined || result === null) {
521
- saveRuleConfigCache.set(uri, null);
522
- return undefined;
523
- } else {
524
- saveRuleConfigCache.set(uri, result);
525
- return result;
526
- }
527
- }
528
- export function remove(key: string): boolean {
529
- return saveRuleConfigCache.delete(key);
530
- }
531
-
532
- export function clear(): void {
533
- saveRuleConfigCache.clear();
534
- }
535
-
536
- function isOff(ruleId: string, matchers: string[]): boolean {
537
- for (const matcher of matchers) {
538
- if (matcher.startsWith('!') && new RegExp(`^${matcher.slice(1).replace(/\*/g, '.*')}$`, 'g').test(ruleId)) {
539
- return true;
540
- } else if (new RegExp(`^${matcher.replace(/\*/g, '.*')}$`, 'g').test(ruleId)) {
541
- return false;
542
- }
543
- }
544
- return true;
545
- }
546
- }
547
-
548
- /**
549
- * Manages rule severity overrides done using VS Code settings.
550
- */
551
- export namespace RuleSeverities {
552
-
553
- const ruleSeverityCache = new LRUCache<string, RuleSeverity | null>(1024);
554
-
555
- export function getOverride(ruleId: string, customizations: RuleCustomization[], isFixable?: boolean): RuleSeverity | undefined {
556
- let result: RuleSeverity | undefined | null = ruleSeverityCache.get(ruleId);
557
- if (result === null) {
558
- return undefined;
559
- }
560
- if (result !== undefined) {
561
- return result;
562
- }
563
- for (const customization of customizations) {
564
- if (
565
- // Rule name should match
566
- asteriskMatches(customization.rule, ruleId) &&
567
- // Fixable flag should match the fixability of the rule if it's defined
568
- (customization.fixable === undefined || customization.fixable === isFixable)
569
- ) {
570
- result = customization.severity;
571
- }
572
- }
573
- if (result === undefined) {
574
- ruleSeverityCache.set(ruleId, null);
575
- return undefined;
576
- }
577
-
578
- ruleSeverityCache.set(ruleId, result);
579
- return result;
580
- }
581
-
582
- export function clear(): void {
583
- ruleSeverityCache.clear();
584
- }
585
-
586
- function asteriskMatches(matcher: string, ruleId: string): boolean {
587
- return matcher.startsWith('!')
588
- ? !(new RegExp(`^${matcher.slice(1).replace(/\*/g, '.*')}$`, 'g').test(ruleId))
589
- : new RegExp(`^${matcher.replace(/\*/g, '.*')}$`, 'g').test(ruleId);
590
- }
591
- }
592
-
593
-
594
- /**
595
- * Creates LSP Diagnostics and captures code action information.
596
- */
597
- namespace Diagnostics {
598
-
599
- export function computeKey(diagnostic: Diagnostic): string {
600
- const range = diagnostic.range;
601
- let message: string | undefined;
602
- if (diagnostic.message) {
603
- const hash = crypto.createHash('sha256');
604
- hash.update(diagnostic.message);
605
- message = hash.digest('base64');
606
- }
607
- return `[${range.start.line},${range.start.character},${range.end.line},${range.end.character}]-${diagnostic.code}-${message ?? ''}`;
608
- }
609
-
610
- export function create(settings: TextDocumentSettings, problem: ESLintProblem, document: TextDocument): [Diagnostic, RuleSeverity | undefined] {
611
- const message = problem.message;
612
- const startLine = typeof problem.line !== 'number' || Number.isNaN(problem.line) ? 0 : Math.max(0, problem.line - 1);
613
- const startChar = typeof problem.column !== 'number' || Number.isNaN(problem.column) ? 0 : Math.max(0, problem.column - 1);
614
- let endLine = typeof problem.endLine !== 'number' || Number.isNaN(problem.endLine) ? startLine : Math.max(0, problem.endLine - 1);
615
- let endChar = typeof problem.endColumn !== 'number' || Number.isNaN(problem.endColumn) ? startChar : Math.max(0, problem.endColumn - 1);
616
- if (settings.problems.shortenToSingleLine && endLine !== startLine) {
617
- const startLineText = document.getText({
618
- start: {
619
- line: startLine,
620
- character: 0,
621
- },
622
- end: {
623
- line: startLine,
624
- character: uinteger.MAX_VALUE,
625
- }
626
- });
627
- endLine = startLine;
628
- endChar = startLineText.length;
629
- }
630
-
631
- const override = RuleSeverities.getOverride(problem.ruleId, settings.rulesCustomizations, problem.fix !== undefined);
632
- const result: Diagnostic = {
633
- message: message,
634
- severity: convertSeverityToDiagnosticWithOverride(problem.severity, override),
635
- source: 'eslint',
636
- range: {
637
- start: { line: startLine, character: startChar },
638
- end: { line: endLine, character: endChar }
639
- }
640
- };
641
- if (problem.ruleId) {
642
- const url = RuleMetaData.getUrl(problem.ruleId);
643
- result.code = problem.ruleId;
644
- if (url !== undefined) {
645
- result.codeDescription = {
646
- href: url
647
- };
648
- }
649
- if (problem.ruleId === 'no-unused-vars') {
650
- result.tags = [DiagnosticTag.Unnecessary];
651
- }
652
- }
653
-
654
- return [result, override];
655
- }
656
-
657
- function adjustSeverityForOverride(severity: number | RuleSeverity, severityOverride?: RuleSeverity) {
658
- switch (severityOverride) {
659
- case RuleSeverity.off:
660
- case RuleSeverity.info:
661
- case RuleSeverity.warn:
662
- case RuleSeverity.error:
663
- return severityOverride;
664
-
665
- case RuleSeverity.downgrade:
666
- switch (convertSeverityToDiagnostic(severity)) {
667
- case DiagnosticSeverity.Error:
668
- return RuleSeverity.warn;
669
- case DiagnosticSeverity.Warning:
670
- case DiagnosticSeverity.Information:
671
- return RuleSeverity.info;
672
- }
673
-
674
- case RuleSeverity.upgrade:
675
- switch (convertSeverityToDiagnostic(severity)) {
676
- case DiagnosticSeverity.Information:
677
- return RuleSeverity.warn;
678
- case DiagnosticSeverity.Warning:
679
- case DiagnosticSeverity.Error:
680
- return RuleSeverity.error;
681
- }
682
-
683
- default:
684
- return severity;
685
- }
686
- }
687
-
688
- function convertSeverityToDiagnostic(severity: number | RuleSeverity) {
689
- // RuleSeverity concerns an overridden rule. A number is direct from ESLint.
690
- switch (severity) {
691
- // Eslint 1 is warning
692
- case 1:
693
- case RuleSeverity.warn:
694
- return DiagnosticSeverity.Warning;
695
- case 2:
696
- case RuleSeverity.error:
697
- return DiagnosticSeverity.Error;
698
- case RuleSeverity.info:
699
- return DiagnosticSeverity.Information;
700
- default:
701
- return DiagnosticSeverity.Error;
702
- }
703
- }
704
-
705
- function convertSeverityToDiagnosticWithOverride(severity: number | RuleSeverity, severityOverride: RuleSeverity | undefined): DiagnosticSeverity {
706
- return convertSeverityToDiagnostic(adjustSeverityForOverride(severity, severityOverride));
707
-
708
- }
709
- }
710
-
711
- /**
712
- * Capture information necessary to compute code actions.
713
- */
714
- export namespace CodeActions {
715
- const codeActions: Map<string, Map<string, Problem>> = new Map<string, Map<string, Problem>>();
716
-
717
- export function get(uri: string): Map<string, Problem> | undefined {
718
- return codeActions.get(uri);
719
- }
720
-
721
- export function set(uri: string, value: Map<string, Problem>): void {
722
- codeActions.set(uri, value);
723
- }
724
-
725
- export function remove(uri: string): boolean {
726
- return codeActions.delete(uri);
727
- }
728
-
729
- export function record(document: TextDocument, diagnostic: Diagnostic, problem: ESLintProblem): void {
730
- if (!problem.ruleId) {
731
- return;
732
- }
733
- const uri = document.uri;
734
- let edits: Map<string, Problem> | undefined = CodeActions.get(uri);
735
- if (edits === undefined) {
736
- edits = new Map<string, Problem>();
737
- CodeActions.set(uri, edits);
738
- }
739
- edits.set(Diagnostics.computeKey(diagnostic), {
740
- label: `Fix this ${problem.ruleId} problem`,
741
- documentVersion: document.version,
742
- ruleId: problem.ruleId,
743
- line: problem.line,
744
- diagnostic: diagnostic,
745
- edit: problem.fix,
746
- suggestions: problem.suggestions
747
- });
748
- }
749
- }
750
-
751
- /**
752
- * Wrapper round the ESLint npm module.
753
- */
754
- export namespace ESLint {
755
-
756
- let connection: ProposedFeatures.Connection;
757
- let documents: TextDocuments<TextDocument>;
758
- let inferFilePath: (documentOrUri: string | TextDocument | URI | undefined, useRealpaths: boolean) => string | undefined;
759
- let loadNodeModule: <T>(moduleName: string) => T | undefined;
760
-
761
- const languageId2ParserRegExp: Map<string, RegExp[]> = function createLanguageId2ParserRegExp() {
762
- const result = new Map<string, RegExp[]>();
763
- const typescript = /\/@typescript-eslint\/parser\//;
764
- const babelESLint = /\/babel-eslint\/lib\/index.js$/;
765
- const vueESLint = /\/vue-eslint-parser\/index.js$/;
766
- result.set('typescript', [typescript, babelESLint, vueESLint]);
767
- result.set('typescriptreact', [typescript, babelESLint, vueESLint]);
768
-
769
- const angular = /\/@angular-eslint\/template-parser\//;
770
- result.set('html', [angular]);
771
-
772
- return result;
773
- }();
774
-
775
- const languageId2ParserOptions: Map<string, { regExps: RegExp[]; parsers: Set<string>; parserRegExps?: RegExp[] }> = function createLanguageId2ParserOptionsRegExp() {
776
- const result = new Map<string, { regExps: RegExp[]; parsers: Set<string>; parserRegExps?: RegExp[] }>();
777
- const vue = /vue-eslint-parser\/.*\.js$/;
778
- const typescriptEslintParser = /@typescript-eslint\/parser\/.*\.js$/;
779
- result.set('typescript', { regExps: [vue], parsers: new Set<string>(['@typescript-eslint/parser']), parserRegExps: [typescriptEslintParser] });
780
- return result;
781
- }();
782
-
783
- const languageId2PluginName: Map<string, string> = new Map([
784
- ['astro', 'astro'],
785
- ['civet', 'civet'],
786
- ['html', 'html'],
787
- ['json', 'jsonc'],
788
- ['json5', 'jsonc'],
789
- ['jsonc', 'jsonc'],
790
- ['mdx', 'mdx'],
791
- ['vue', 'vue'],
792
- ['markdown', 'markdown'],
793
- ['css', 'css'],
794
- ['glimmer-js', 'ember'],
795
- ['glimmer-ts', 'ember'],
796
- ['svelte', 'svelte'],
797
- ]);
798
-
799
- const defaultLanguageIds: Set<string> = new Set([
800
- 'javascript', 'javascriptreact'
801
- ]);
802
-
803
- const projectFolderIndicators: {
804
- fileName: string;
805
- isRoot: boolean;
806
- isFlatConfig: boolean;
807
- }[] = [
808
- { fileName: 'eslint.config.js', isRoot: true, isFlatConfig: true },
809
- { fileName: 'eslint.config.cjs', isRoot: true, isFlatConfig: true },
810
- { fileName: 'eslint.config.mjs', isRoot: true, isFlatConfig: true },
811
- { fileName: 'eslint.config.ts', isRoot: true, isFlatConfig: true },
812
- { fileName: 'eslint.config.cts', isRoot: true, isFlatConfig: true },
813
- { fileName: 'eslint.config.mts', isRoot: true, isFlatConfig: true },
814
- { fileName: 'package.json', isRoot: true, isFlatConfig: false },
815
- { fileName: '.eslintignore', isRoot: true, isFlatConfig: false },
816
- { fileName: '.eslintrc', isRoot: false, isFlatConfig: false },
817
- { fileName: '.eslintrc.json', isRoot: false, isFlatConfig: false },
818
- { fileName: '.eslintrc.js', isRoot: false, isFlatConfig: false },
819
- { fileName: '.eslintrc.yaml', isRoot: false, isFlatConfig: false },
820
- { fileName: '.eslintrc.yml', isRoot: false, isFlatConfig: false }
821
- ];
822
-
823
- const path2Library: Map<string, ESLintModule> = new Map<string, ESLintModule>();
824
- const document2Settings: Map<string, Promise<TextDocumentSettings>> = new Map<string, Promise<TextDocumentSettings>>();
825
- const formatterRegistrations: Map<string, Promise<Disposable>> = new Map();
826
-
827
- export function initialize($connection: ProposedFeatures.Connection, $documents: TextDocuments<TextDocument>, $inferFilePath: (documentOrUri: string | TextDocument | URI | undefined, useRealpaths: boolean) => string | undefined, $loadNodeModule: <T>(moduleName: string) => T | undefined) {
828
- connection = $connection;
829
- documents = $documents;
830
- inferFilePath = $inferFilePath;
831
- loadNodeModule = $loadNodeModule;
832
- }
833
-
834
- export function removeSettings(key: string): boolean {
835
- return document2Settings.delete(key);
836
- }
837
-
838
- export function clearSettings(): void {
839
- document2Settings.clear();
840
- }
841
-
842
- export function unregisterAsFormatter(document: TextDocument): void {
843
- const unregister = formatterRegistrations.get(document.uri);
844
- if (unregister !== undefined) {
845
- void unregister.then(disposable => disposable.dispose());
846
- formatterRegistrations.delete(document.uri);
847
- }
848
- }
849
-
850
- export function clearFormatters(): void {
851
- for (const unregistration of formatterRegistrations.values()) {
852
- void unregistration.then(disposable => disposable.dispose());
853
- }
854
- formatterRegistrations.clear();
855
- }
856
-
857
- export function resolveSettings(document: TextDocument): Promise<TextDocumentSettings> {
858
- const uri = document.uri;
859
- let resultPromise = document2Settings.get(uri);
860
- if (resultPromise) {
861
- return resultPromise;
862
- }
863
- resultPromise = connection.workspace.getConfiguration({ scopeUri: uri, section: '' }).then((configuration: ConfigurationSettings) => {
864
- const settings: TextDocumentSettings = Object.assign(
865
- {},
866
- configuration,
867
- { silent: false, library: undefined, resolvedGlobalPackageManagerPath: undefined },
868
- { workingDirectory: undefined}
869
- );
870
- if (settings.validate === Validate.off) {
871
- return settings;
872
- }
873
- settings.resolvedGlobalPackageManagerPath = GlobalPaths.get(settings.packageManager);
874
- const filePath = inferFilePath(document, settings.useRealpaths);
875
- const workspaceFolderPath = settings.workspaceFolder !== undefined ? inferFilePath(settings.workspaceFolder.uri, settings.useRealpaths) : undefined;
876
- let assumeFlatConfig:boolean = false;
877
- const hasUserDefinedWorkingDirectories: boolean = configuration.workingDirectory !== undefined;
878
- const workingDirectoryConfig = configuration.workingDirectory ?? { mode: ModeEnum.location };
879
- if (ModeItem.is(workingDirectoryConfig)) {
880
- let candidate: string | undefined;
881
- if (workingDirectoryConfig.mode === ModeEnum.location) {
882
- if (workspaceFolderPath !== undefined) {
883
- const [configLocation, isFlatConfig] = findWorkingDirectory(workspaceFolderPath, filePath);
884
- if (isFlatConfig && settings.useFlatConfig !== false) {
885
- candidate = configLocation;
886
- assumeFlatConfig = true;
887
- } else {
888
- candidate = workspaceFolderPath;
889
- }
890
- } else if (filePath !== undefined && !isUNC(filePath)) {
891
- candidate = path.dirname(filePath);
892
- }
893
- } else if (workingDirectoryConfig.mode === ModeEnum.auto) {
894
- if (workspaceFolderPath !== undefined) {
895
- candidate = findWorkingDirectory(workspaceFolderPath, filePath)[0];
896
- } else if (filePath !== undefined && !isUNC(filePath)) {
897
- candidate = path.dirname(filePath);
898
- }
899
- }
900
- if (candidate !== undefined && fs.existsSync(candidate)) {
901
- settings.workingDirectory = { directory: candidate };
902
- }
903
- } else {
904
- settings.workingDirectory = workingDirectoryConfig;
905
- }
906
- let nodePath: string | undefined;
907
- if (settings.nodePath !== null) {
908
- nodePath = settings.nodePath;
909
- if (!path.isAbsolute(nodePath) && workspaceFolderPath !== undefined) {
910
- nodePath = path.join(workspaceFolderPath, nodePath);
911
- }
912
- }
913
- let moduleResolveWorkingDirectory: string | undefined;
914
- if (!hasUserDefinedWorkingDirectories && filePath !== undefined) {
915
- moduleResolveWorkingDirectory = path.dirname(filePath);
916
- }
917
- if (moduleResolveWorkingDirectory === undefined && settings.workingDirectory !== undefined && !settings.workingDirectory['!cwd']) {
918
- moduleResolveWorkingDirectory = settings.workingDirectory.directory;
919
- }
920
-
921
- let promise: Promise<string>;
922
- // During Flat Config is considered experimental,
923
- // we need to import FlatESLint from 'eslint/use-at-your-own-risk'.
924
- // See: https://eslint.org/blog/2022/08/new-config-system-part-3/
925
- const eslintPath = settings.experimental?.useFlatConfig ? 'eslint/use-at-your-own-risk' : 'eslint';
926
- if (nodePath !== undefined) {
927
- promise = Files.resolve(eslintPath, nodePath, nodePath, trace).then<string, string>(undefined, () => {
928
- return Files.resolve(eslintPath, settings.resolvedGlobalPackageManagerPath, moduleResolveWorkingDirectory, trace);
929
- });
930
- } else {
931
- promise = Files.resolve(eslintPath, settings.resolvedGlobalPackageManagerPath, moduleResolveWorkingDirectory, trace);
932
- }
933
-
934
- settings.silent = settings.validate === Validate.probe;
935
- return promise.then(async (libraryPath) => {
936
- let library = path2Library.get(libraryPath);
937
- if (library === undefined) {
938
- if (settings.experimental?.useFlatConfig === true) {
939
- const lib = loadNodeModule<{ FlatESLint?: ESLintClassConstructor }>(libraryPath);
940
- if (lib === undefined) {
941
- settings.validate = Validate.off;
942
- if (!settings.silent) {
943
- connection.console.error(`Failed to load eslint library from ${libraryPath}. If you are using ESLint v8.21 or earlier, try upgrading it. For newer versions, try disabling the 'eslint.experimental.useFlatConfig' setting. See the output panel for more information.`);
944
- }
945
- } else if (lib.FlatESLint === undefined) {
946
- settings.validate = Validate.off;
947
- connection.console.error(`The eslint library loaded from ${libraryPath} doesn\'t export a FlatESLint class.`);
948
- } else {
949
- connection.console.info(`ESLint library loaded from: ${libraryPath}`);
950
- // pretend to be a regular eslint endpoint
951
- library = {
952
- ESLint: lib.FlatESLint,
953
- isFlatConfig: true,
954
- CLIEngine: undefined,
955
- };
956
- settings.library = library;
957
- path2Library.set(libraryPath, library);
958
- }
959
- } else {
960
- library = loadNodeModule(libraryPath);
961
- if (library === undefined) {
962
- settings.validate = Validate.off;
963
- if (!settings.silent) {
964
- connection.console.error(`Failed to load eslint library from ${libraryPath}. See output panel for more information.`);
965
- }
966
- } else if (library.CLIEngine === undefined && library.ESLint === undefined) {
967
- settings.validate = Validate.off;
968
- connection.console.error(`The eslint library loaded from ${libraryPath} doesn\'t export neither a CLIEngine nor an ESLint class. You need at least eslint@1.0.0`);
969
- } else {
970
- connection.console.info(`ESLint library loaded from: ${libraryPath}`);
971
- settings.library = library;
972
- path2Library.set(libraryPath, library);
973
- }
974
- }
975
- if (library !== undefined && ESLintModule.hasESLintClass(library) && typeof library.ESLint.version === 'string') {
976
- const esLintVersion = semverParse(library.ESLint.version);
977
- if (esLintVersion !== null) {
978
- if (semverGte(esLintVersion, '8.57.0') && settings.experimental?.useFlatConfig === true) {
979
- connection.console.info(`ESLint version ${library.ESLint.version} supports flat config without experimental opt-in. The 'eslint.experimental.useFlatConfig' setting can be removed.`);
980
- } else if (semverGte(esLintVersion, '10.0.0') && (settings.experimental?.useFlatConfig === false || settings.useFlatConfig === false)) {
981
- connection.console.info(`ESLint version ${library.ESLint.version} only supports flat configs. Setting is ignored.`);
982
- }
983
- }
984
- }
985
- } else {
986
- settings.library = library;
987
- }
988
- if (settings.validate === Validate.probe && TextDocumentSettings.hasLibrary(settings)) {
989
- settings.validate = Validate.off;
990
- const filePath = ESLint.getFilePath(document, settings);
991
- if (filePath !== undefined) {
992
- const parserRegExps = languageId2ParserRegExp.get(document.languageId);
993
- const pluginName = languageId2PluginName.get(document.languageId);
994
- const parserOptions = languageId2ParserOptions.get(document.languageId);
995
- if (defaultLanguageIds.has(document.languageId)) {
996
- try {
997
- const [isIgnored, configType] = await ESLint.withClass(async (eslintClass) => {
998
- return [await eslintClass.isPathIgnored(filePath), ESLintClass.getConfigType(eslintClass)];
999
- }, settings);
1000
- if (isIgnored === false || (isIgnored === true && settings.onIgnoredFiles !== ESLintSeverity.off)) {
1001
- settings.validate = Validate.on;
1002
- if (assumeFlatConfig && configType === 'eslintrc') {
1003
- connection.console.info(`Expected to use flat configuration from directory ${settings.workingDirectory?.directory} but loaded eslintrc config.`);
1004
- }
1005
- }
1006
- } catch (error: any) {
1007
- settings.validate = Validate.off;
1008
- await connection.sendNotification(StatusNotification.type, { uri, state: Status.error });
1009
- connection.console.error(`Calculating config file for ${uri}) failed.\n${error instanceof Error ? error.stack : ''}`);
1010
- }
1011
- } else if (parserRegExps !== undefined || pluginName !== undefined || parserOptions !== undefined) {
1012
- const [eslintConfig, configType] = await ESLint.withClass(async (eslintClass) => {
1013
- try {
1014
- if (await eslintClass.isPathIgnored(filePath)) {
1015
- return [undefined, undefined];
1016
- } else {
1017
- return [await eslintClass.calculateConfigForFile(filePath), ESLintClass.getConfigType(eslintClass)];
1018
- }
1019
- } catch (err) {
1020
- try {
1021
- await connection.sendNotification(StatusNotification.type, { uri, state: Status.error });
1022
- connection.console.error(`Calculating config file for ${uri}) failed.\n${err instanceof Error ? err.stack : ''}`);
1023
- } catch {
1024
- // little we can do here
1025
- }
1026
- return [undefined, undefined];
1027
- }
1028
- }, settings);
1029
- if (eslintConfig !== undefined) {
1030
- if (assumeFlatConfig && configType === 'eslintrc') {
1031
- connection.console.info(`Expected to use flat configuration from directory ${settings.workingDirectory?.directory} but loaded eslintrc config.`);
1032
- }
1033
- if (configType === 'flat' || ESLintModule.isFlatConfig(settings.library)) {
1034
- // We have a flat configuration. This means that the config file needs to
1035
- // have a section per file extension we want to validate. If there is none than
1036
- // `calculateConfigForFile` will return no config since the config options without
1037
- // a `files` property only applies to `**/*.js, **/*.cjs, and **/*.mjs` by default
1038
- // See https://eslint.org/docs/latest/user-guide/configuring/configuration-files-new#specifying-files-and-ignores
1039
-
1040
- // This means since we have found a configuration for the given file we assume that
1041
- // that configuration is correctly pointing to a parser.
1042
- settings.validate = Validate.on;
1043
- } else {
1044
- const parser: string | undefined = eslintConfig.parser !== null
1045
- ? normalizePath(eslintConfig.parser)
1046
- : undefined;
1047
- if (parser !== undefined) {
1048
- if (parserRegExps !== undefined) {
1049
- for (const regExp of parserRegExps) {
1050
- if (regExp.test(parser)) {
1051
- settings.validate = Validate.on;
1052
- break;
1053
- }
1054
- }
1055
- }
1056
- if (settings.validate !== Validate.on && parserOptions !== undefined && typeof eslintConfig.parserOptions?.parser === 'string') {
1057
- const eslintConfigParserOptionsParser = normalizePath(eslintConfig.parserOptions.parser);
1058
- for (const regExp of parserOptions.regExps) {
1059
- if (regExp.test(parser) && (
1060
- parserOptions.parsers.has(eslintConfig.parserOptions.parser) ||
1061
- parserOptions.parserRegExps !== undefined && parserOptions.parserRegExps.some(parserRegExp => parserRegExp.test(eslintConfigParserOptionsParser))
1062
- )) {
1063
- settings.validate = Validate.on;
1064
- break;
1065
- }
1066
- }
1067
- }
1068
- }
1069
- if (settings.validate !== Validate.on && Array.isArray(eslintConfig.plugins) && eslintConfig.plugins.length > 0 && pluginName !== undefined) {
1070
- for (const name of eslintConfig.plugins) {
1071
- if (name === pluginName) {
1072
- settings.validate = Validate.on;
1073
- break;
1074
- }
1075
- }
1076
- }
1077
- }
1078
- }
1079
- }
1080
- }
1081
- if (settings.validate === Validate.off) {
1082
- const params: ProbeFailedParams = { textDocument: { uri: document.uri } };
1083
- void connection.sendRequest(ProbeFailedRequest.type, params);
1084
- }
1085
- }
1086
- if (settings.validate === Validate.on) {
1087
- settings.silent = false;
1088
- if (settings.format && TextDocumentSettings.hasLibrary(settings) && !formatterRegistrations.has(uri)) {
1089
- const Uri = URI.parse(uri);
1090
- const isFile = Uri.scheme === 'file';
1091
- let pattern: string = isFile
1092
- ? Uri.fsPath.replace(/\\/g, '/')
1093
- : Uri.fsPath;
1094
- pattern = pattern.replace(/[\[\]\{\}]/g, '?');
1095
-
1096
- const filter: DocumentFilter = { scheme: Uri.scheme, pattern: pattern };
1097
- const options: DocumentFormattingRegistrationOptions = { documentSelector: [filter] };
1098
- if (!isFile) {
1099
- formatterRegistrations.set(uri, connection.client.register(DocumentFormattingRequest.type, options));
1100
- } else {
1101
- const filePath = inferFilePath(uri, settings.useRealpaths)!;
1102
- await ESLint.withClass(async (eslintClass) => {
1103
- if (!await eslintClass.isPathIgnored(filePath)) {
1104
- formatterRegistrations.set(uri, connection.client.register(DocumentFormattingRequest.type, options));
1105
- }
1106
- }, settings);
1107
- }
1108
- }
1109
- }
1110
- return settings;
1111
- }, () => {
1112
- settings.validate = Validate.off;
1113
- if (!settings.silent) {
1114
- void connection.sendRequest(NoESLintLibraryRequest.type, { source: { uri: document.uri } });
1115
- }
1116
- return settings;
1117
- });
1118
- });
1119
- document2Settings.set(uri, resultPromise);
1120
- return resultPromise;
1121
- }
1122
-
1123
- export async function newClass(library: ESLintModule, newOptions: ESLintClassOptions | CLIOptions, settings: TextDocumentSettings): Promise<ESLintClass> {
1124
- // Since ESLint version 8.57 we have a dedicated loadESLint function
1125
- // which takes care of loading the right ESLint class. We available
1126
- // we use it.
1127
- if (ESLintModule.hasLoadESLint(library)) {
1128
- return new (await library.loadESLint({ useFlatConfig: settings.useFlatConfig }))(newOptions);
1129
- }
1130
- // If we have version 7 where we have both ESLint class and CLIEngine we only
1131
- // use the ESLint class if a corresponding setting (useESLintClass) is set.
1132
- if (ESLintModule.hasESLintClass(library) && settings.useESLintClass) {
1133
- return new library.ESLint(newOptions);
1134
- }
1135
- if (ESLintModule.hasCLIEngine(library)) {
1136
- return new ESLintClassEmulator(new library.CLIEngine(newOptions));
1137
- }
1138
- return new library.ESLint(newOptions);
1139
- }
1140
-
1141
- export async function withClass<T>(func: (eslintClass: ESLintClass) => Promise<T>, settings: TextDocumentSettings & { library: ESLintModule }, options?: ESLintClassOptions | CLIOptions): Promise<T> {
1142
- const newOptions: ESLintClassOptions | CLIOptions = options === undefined
1143
- ? Object.assign(Object.create(null), settings.options)
1144
- : Object.assign(Object.create(null), settings.options, options);
1145
-
1146
- const cwd = process.cwd();
1147
- try {
1148
- if (settings.workingDirectory) {
1149
- // A lot of libs are sensitive to drive letter casing and assume a
1150
- // upper case drive letter. Make sure we support that correctly.
1151
- const newCWD = normalizeWorkingDirectory(settings.workingDirectory.directory);
1152
- newOptions.cwd = newCWD;
1153
- if (settings.workingDirectory['!cwd'] !== true && fs.existsSync(newCWD)) {
1154
- process.chdir(newCWD);
1155
- }
1156
- }
1157
-
1158
- const eslintClass = await newClass(settings.library, newOptions, settings);
1159
- // We need to await the result to ensure proper execution of the
1160
- // finally block.
1161
- return await func(eslintClass);
1162
- } finally {
1163
- if (cwd !== process.cwd()) {
1164
- process.chdir(cwd);
1165
- }
1166
- }
1167
- }
1168
-
1169
- function normalizeWorkingDirectory(value: string): string {
1170
- const result = normalizeDriveLetter(value);
1171
- if (result.length === 0) {
1172
- return result;
1173
- }
1174
- return result[result.length - 1] === path.sep
1175
- ? result.substring(0, result.length - 1)
1176
- : result;
1177
- }
1178
-
1179
- export function getFilePath(document: TextDocument | undefined, settings: TextDocumentSettings): string | undefined {
1180
- if (document === undefined) {
1181
- return undefined;
1182
- }
1183
- const uri = URI.parse(document.uri);
1184
- if (uri.scheme !== 'file') {
1185
- if (settings.workspaceFolder !== undefined) {
1186
- const ext = LanguageDefaults.getExtension(document.languageId);
1187
- const workspacePath = inferFilePath(settings.workspaceFolder.uri, settings.useRealpaths);
1188
- if (workspacePath !== undefined && ext !== undefined) {
1189
- return path.join(workspacePath, `test.${ext}`);
1190
- }
1191
- }
1192
- return undefined;
1193
- } else {
1194
- return inferFilePath(uri, settings.useRealpaths);
1195
- }
1196
- }
1197
-
1198
- const validFixTypes = new Set<string>(['problem', 'suggestion', 'layout', 'directive']);
1199
- export async function validate(document: TextDocument, settings: TextDocumentSettings & { library: ESLintModule }): Promise<Diagnostic[]> {
1200
- const newOptions: CLIOptions = Object.assign(Object.create(null), settings.options);
1201
- let fixTypes: Set<string> | undefined = undefined;
1202
- if (Array.isArray(newOptions.fixTypes) && newOptions.fixTypes.length > 0) {
1203
- fixTypes = new Set();
1204
- for (const item of newOptions.fixTypes) {
1205
- if (validFixTypes.has(item)) {
1206
- fixTypes.add(item);
1207
- }
1208
- }
1209
- if (fixTypes.size === 0) {
1210
- fixTypes = undefined;
1211
- }
1212
- }
1213
-
1214
- const content = document.getText();
1215
- const uri = document.uri;
1216
- const file = getFilePath(document, settings);
1217
-
1218
- return withClass(async (eslintClass) => {
1219
- CodeActions.remove(uri);
1220
- const reportResults: ESLintDocumentReport[] = await eslintClass.lintText(content, { filePath: file, warnIgnored: settings.onIgnoredFiles !== ESLintSeverity.off });
1221
- RuleMetaData.capture(eslintClass, reportResults);
1222
- const diagnostics: Diagnostic[] = [];
1223
- if (reportResults && Array.isArray(reportResults) && reportResults.length > 0) {
1224
- const docReport = reportResults[0];
1225
- if (docReport.messages && Array.isArray(docReport.messages)) {
1226
- docReport.messages.forEach((problem) => {
1227
- if (problem) {
1228
- const [diagnostic, override] = Diagnostics.create(settings, problem, document);
1229
- if (!(override === RuleSeverity.off || (settings.quiet && (diagnostic.severity === DiagnosticSeverity.Warning || diagnostic.severity === DiagnosticSeverity.Information)))) {
1230
- diagnostics.push(diagnostic);
1231
- }
1232
- if (fixTypes !== undefined && problem.ruleId !== undefined && problem.fix !== undefined) {
1233
- const type = RuleMetaData.getType(problem.ruleId);
1234
- if (type !== undefined && fixTypes.has(type)) {
1235
- CodeActions.record(document, diagnostic, problem);
1236
- }
1237
- } else {
1238
- if (RuleMetaData.isUnusedDisableDirectiveProblem(problem)) {
1239
- problem.ruleId = RuleMetaData.unusedDisableDirectiveId;
1240
- }
1241
-
1242
- CodeActions.record(document, diagnostic, problem);
1243
- }
1244
- }
1245
- });
1246
- }
1247
- }
1248
- return diagnostics;
1249
- }, settings);
1250
- }
1251
-
1252
- function trace(message: string, verbose?: string): void {
1253
- connection.tracer.log(message, verbose);
1254
- }
1255
-
1256
- /**
1257
- * Global paths for the different package managers
1258
- */
1259
- namespace GlobalPaths {
1260
- const globalPaths: Record<string, { cache: string | undefined; get(): string | undefined }> = {
1261
- yarn: {
1262
- cache: undefined,
1263
- get(): string | undefined {
1264
- return Files.resolveGlobalYarnPath(trace);
1265
- }
1266
- },
1267
- npm: {
1268
- cache: undefined,
1269
- get(): string | undefined {
1270
- return Files.resolveGlobalNodePath(trace);
1271
- }
1272
- },
1273
- pnpm: {
1274
- cache: undefined,
1275
- get(): string {
1276
- const pnpmPath = execSync('pnpm root -g').toString().trim();
1277
- return pnpmPath;
1278
- }
1279
- }
1280
- };
1281
-
1282
- export function get(packageManager: PackageManagers): string | undefined {
1283
- const pm = globalPaths[packageManager];
1284
- if (pm) {
1285
- if (pm.cache === undefined) {
1286
- pm.cache = pm.get();
1287
- }
1288
- return pm.cache;
1289
- }
1290
- return undefined;
1291
- }
1292
- }
1293
-
1294
- export function findWorkingDirectory(workspaceFolder: string, file: string | undefined): [string, boolean] {
1295
- if (file === undefined || isUNC(file)) {
1296
- return [workspaceFolder, false];
1297
- }
1298
- // Don't probe for something in node modules folder.
1299
- if (file.indexOf(`${path.sep}node_modules${path.sep}`) !== -1) {
1300
- return [workspaceFolder, false];
1301
- }
1302
-
1303
- let result: string = workspaceFolder;
1304
- let flatConfig: boolean = false;
1305
- let directory: string | undefined = path.dirname(file);
1306
- outer: while (directory !== undefined && directory.startsWith(workspaceFolder)) {
1307
- for (const { fileName, isRoot, isFlatConfig } of projectFolderIndicators) {
1308
- if (fs.existsSync(path.join(directory, fileName))) {
1309
- result = directory;
1310
- flatConfig = isFlatConfig;
1311
- if (isRoot) {
1312
- break outer;
1313
- } else {
1314
- break;
1315
- }
1316
- }
1317
- }
1318
- const parent = path.dirname(directory);
1319
- directory = parent !== directory ? parent : undefined;
1320
- }
1321
- return [result, flatConfig];
1322
- }
1323
-
1324
- export namespace ErrorHandlers {
1325
-
1326
- export const single: ((error: any, document: TextDocument, library: ESLintModule, settings: TextDocumentSettings) => Status | undefined)[] = [
1327
- tryHandleNoConfig,
1328
- tryHandleConfigError,
1329
- tryHandleMissingModule,
1330
- showErrorMessage
1331
- ];
1332
-
1333
- export function getMessage(err: any, document: TextDocument): string {
1334
- let result: string | undefined = undefined;
1335
- if (typeof err.message === 'string' || err.message instanceof String) {
1336
- result = <string>err.message;
1337
- result = result.replace(/\r?\n/g, ' ');
1338
- if (/^CLI: /.test(result)) {
1339
- result = result.substr(5);
1340
- }
1341
- } else {
1342
- result = `An unknown error occurred while validating document: ${document.uri}`;
1343
- }
1344
- return result;
1345
- }
1346
-
1347
- const noConfigReported: Map<string, ESLintModule> = new Map<string, ESLintModule>();
1348
-
1349
- export function clearNoConfigReported(): void {
1350
- noConfigReported.clear();
1351
- }
1352
-
1353
- function tryHandleNoConfig(error: any, document: TextDocument, library: ESLintModule): Status | undefined {
1354
- if (!ESLintError.isNoConfigFound(error)) {
1355
- return undefined;
1356
- }
1357
- if (!noConfigReported.has(document.uri)) {
1358
- connection.sendRequest(
1359
- NoConfigRequest.type,
1360
- {
1361
- message: getMessage(error, document),
1362
- document: {
1363
- uri: document.uri
1364
- }
1365
- }
1366
- ).then(undefined, () => { });
1367
- noConfigReported.set(document.uri, library);
1368
- }
1369
- return Status.warn;
1370
- }
1371
-
1372
- const configErrorReported: Map<string, { library: ESLintModule; settings: TextDocumentSettings }> = new Map();
1373
-
1374
- export function getConfigErrorReported(key: string): { library: ESLintModule; settings: TextDocumentSettings } | undefined {
1375
- return configErrorReported.get(key);
1376
- }
1377
-
1378
- export function removeConfigErrorReported(key: string): boolean {
1379
- return configErrorReported.delete(key);
1380
- }
1381
-
1382
- function tryHandleConfigError(error: any, document: TextDocument, library: ESLintModule, settings: TextDocumentSettings): Status | undefined {
1383
- if (!error.message) {
1384
- return undefined;
1385
- }
1386
-
1387
- function handleFileName(filename: string): Status {
1388
- if (!configErrorReported.has(filename)) {
1389
- connection.console.error(getMessage(error, document));
1390
- if (!documents.get(URI.file(filename).toString())) {
1391
- connection.window.showInformationMessage(getMessage(error, document));
1392
- }
1393
- configErrorReported.set(filename, { library, settings });
1394
- }
1395
- return Status.warn;
1396
- }
1397
-
1398
- let matches = /Cannot read config file:\s+(.*)\nError:\s+(.*)/.exec(error.message);
1399
- if (matches && matches.length === 3) {
1400
- return handleFileName(matches[1]);
1401
- }
1402
-
1403
- matches = /(.*):\n\s*Configuration for rule \"(.*)\" is /.exec(error.message);
1404
- if (matches && matches.length === 3) {
1405
- return handleFileName(matches[1]);
1406
- }
1407
-
1408
- matches = /Cannot find module '([^']*)'\nReferenced from:\s+(.*)/.exec(error.message);
1409
- if (matches && matches.length === 3) {
1410
- return handleFileName(matches[2]);
1411
- }
1412
-
1413
- return undefined;
1414
- }
1415
-
1416
- const missingModuleReported: Map<string, ESLintModule> = new Map<string, ESLintModule>();
1417
-
1418
- export function clearMissingModuleReported(): void {
1419
- missingModuleReported.clear();
1420
- }
1421
-
1422
- function tryHandleMissingModule(error: any, document: TextDocument, library: ESLintModule, settings: TextDocumentSettings): Status | undefined {
1423
- if (!error.message) {
1424
- return undefined;
1425
- }
1426
-
1427
- function handleMissingModule(plugin: string, module: string, error: ESLintError): Status {
1428
- if (!missingModuleReported.has(plugin)) {
1429
- const fsPath = inferFilePath(document, settings.useRealpaths);
1430
- missingModuleReported.set(plugin, library);
1431
- if (error.messageTemplate === 'plugin-missing') {
1432
- connection.console.error([
1433
- '',
1434
- `${error.message.toString()}`,
1435
- `Happened while validating ${fsPath ? fsPath : document.uri}`,
1436
- `This can happen for a couple of reasons:`,
1437
- `1. The plugin name is spelled incorrectly in an ESLint configuration file (e.g. .eslintrc).`,
1438
- `2. If ESLint is installed globally, then make sure ${module} is installed globally as well.`,
1439
- `3. If ESLint is installed locally, then ${module} isn't installed correctly.`,
1440
- '',
1441
- `Consider running eslint --debug ${fsPath ? fsPath : document.uri} from a terminal to obtain a trace about the configuration files used.`
1442
- ].join('\n'));
1443
- } else {
1444
- connection.console.error([
1445
- `${error.message.toString()}`,
1446
- `Happened while validating ${fsPath ? fsPath : document.uri}`
1447
- ].join('\n'));
1448
- }
1449
- }
1450
- return Status.warn;
1451
- }
1452
-
1453
- const matches = /Failed to load plugin (.*): Cannot find module (.*)/.exec(error.message);
1454
- if (matches && matches.length === 3) {
1455
- return handleMissingModule(matches[1], matches[2], error);
1456
- }
1457
-
1458
- return undefined;
1459
- }
1460
-
1461
- function showErrorMessage(error: any, document: TextDocument): Status {
1462
- if (Is.string(error.stack)) {
1463
- connection.console.error('An unexpected error occurred:');
1464
- connection.console.error(error.stack);
1465
- } else {
1466
- connection.console.error(`An unexpected error occurred: ${getMessage(error, document)}.`);
1467
- }
1468
- return Status.error;
1469
- }
1470
- }
1471
- }