Skip to content

Commit d910fef

Browse files
authored
(feat) possibility to ignore compiler warnings/treat them as errors (#476)
#473
1 parent fd559c5 commit d910fef

File tree

10 files changed

+189
-27
lines changed

10 files changed

+189
-27
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export * from './server';
22
export { offsetAt } from './lib/documents';
3-
export { SvelteCheck } from './svelte-check';
3+
export { SvelteCheck, SvelteCheckOptions } from './svelte-check';

packages/language-server/src/ls-config.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const defaultLSConfig: LSConfig = {
3232
},
3333
svelte: {
3434
enable: true,
35+
compilerWarnings: {},
3536
diagnostics: { enable: true },
3637
format: { enable: true },
3738
completions: { enable: true },
@@ -114,8 +115,11 @@ export interface LSHTMLConfig {
114115
};
115116
}
116117

118+
export type CompilerWarningsSettings = Record<string, 'ignore' | 'error'>;
119+
117120
export interface LSSvelteConfig {
118121
enable: boolean;
122+
compilerWarnings: CompilerWarningsSettings;
119123
diagnostics: {
120124
enable: boolean;
121125
};
@@ -133,25 +137,51 @@ export interface LSSvelteConfig {
133137
};
134138
}
135139

140+
type DeepPartial<T> = T extends CompilerWarningsSettings
141+
? T
142+
: {
143+
[P in keyof T]?: DeepPartial<T[P]>;
144+
};
145+
136146
export class LSConfigManager {
137147
private config: LSConfig = defaultLSConfig;
138148

139149
/**
140150
* Updates config.
141151
*/
142-
update(config: LSConfig): void {
152+
update(config: DeepPartial<LSConfig>): void {
143153
// Ideally we shouldn't need the merge here because all updates should be valid and complete configs.
144154
// But since those configs come from the client they might be out of synch with the valid config:
145155
// We might at some point in the future forget to synch config settings in all packages after updating the config.
146156
this.config = merge({}, defaultLSConfig, this.config, config);
157+
// Merge will keep arrays/objects if the new one is empty/has less entries,
158+
// therefore we need some extra checks if there are new settings
159+
if (config.svelte?.compilerWarnings) {
160+
this.config.svelte.compilerWarnings = config.svelte.compilerWarnings;
161+
}
147162
}
148163

149164
/**
150165
* Whether or not specified config is enabled
151166
* @param key a string which is a path. Example: 'svelte.diagnostics.enable'.
152167
*/
153168
enabled(key: string): boolean {
154-
return !!get(this.config, key);
169+
return !!this.get(key);
170+
}
171+
172+
/**
173+
* Get specific config
174+
* @param key a string which is a path. Example: 'svelte.diagnostics.enable'.
175+
*/
176+
get<T>(key: string): T {
177+
return get(this.config, key);
178+
}
179+
180+
/**
181+
* Get the whole config
182+
*/
183+
getConfig(): Readonly<LSConfig> {
184+
return this.config;
155185
}
156186
}
157187

packages/language-server/src/plugins/svelte/SveltePlugin.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { cosmiconfig } from 'cosmiconfig';
21
import {
32
CodeAction,
43
CodeActionContext,
@@ -34,10 +33,6 @@ export class SveltePlugin
3433
HoverProvider,
3534
CodeActionsProvider {
3635
private docManager = new Map<Document, SvelteDocument>();
37-
private cosmiConfigExplorer = cosmiconfig('svelte', {
38-
packageProp: 'svelte-ls',
39-
cache: true,
40-
});
4136

4237
constructor(private configManager: LSConfigManager, private prettierConfig: any) {}
4338

@@ -46,7 +41,11 @@ export class SveltePlugin
4641
return [];
4742
}
4843

49-
return getDiagnostics(document, await this.getSvelteDoc(document));
44+
return getDiagnostics(
45+
document,
46+
await this.getSvelteDoc(document),
47+
this.configManager.getConfig().svelte.compilerWarnings,
48+
);
5049
}
5150

5251
async getCompiledResult(document: Document): Promise<SvelteCompileResult | null> {

packages/language-server/src/plugins/svelte/features/getDiagnostics.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Diagnostic, DiagnosticSeverity, Position, Range } from 'vscode-language
33
import { Document, isInTag, mapDiagnosticToOriginal } from '../../../lib/documents';
44
import { Logger } from '../../../logger';
55
import { SvelteDocument, TranspileErrorSource } from '../SvelteDocument';
6+
import { CompilerWarningsSettings } from '../../../ls-config';
67

78
/**
89
* Returns diagnostics from the svelte compiler.
@@ -11,13 +12,14 @@ import { SvelteDocument, TranspileErrorSource } from '../SvelteDocument';
1112
export async function getDiagnostics(
1213
document: Document,
1314
svelteDoc: SvelteDocument,
15+
settings: CompilerWarningsSettings,
1416
): Promise<Diagnostic[]> {
1517
if (svelteDoc.config.loadConfigError) {
1618
return getConfigLoadErrorDiagnostics(svelteDoc.config.loadConfigError);
1719
}
1820

1921
try {
20-
return await tryGetDiagnostics(document, svelteDoc);
22+
return await tryGetDiagnostics(document, svelteDoc, settings);
2123
} catch (error) {
2224
return getPreprocessErrorDiagnostics(document, error);
2325
}
@@ -29,19 +31,24 @@ export async function getDiagnostics(
2931
async function tryGetDiagnostics(
3032
document: Document,
3133
svelteDoc: SvelteDocument,
34+
settings: CompilerWarningsSettings,
3235
): Promise<Diagnostic[]> {
3336
const transpiled = await svelteDoc.getTranspiled();
3437

3538
try {
3639
const res = await svelteDoc.getCompiled();
3740
return (((res.stats as any).warnings || res.warnings || []) as Warning[])
41+
.filter((warning) => settings[warning.code] !== 'ignore')
3842
.map((warning) => {
3943
const start = warning.start || { line: 1, column: 0 };
4044
const end = warning.end || start;
4145
return {
4246
range: Range.create(start.line - 1, start.column, end.line - 1, end.column),
4347
message: warning.message,
44-
severity: DiagnosticSeverity.Warning,
48+
severity:
49+
settings[warning.code] === 'error'
50+
? DiagnosticSeverity.Error
51+
: DiagnosticSeverity.Warning,
4552
source: 'svelte',
4653
code: warning.code,
4754
};

packages/language-server/src/svelte-check.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { Diagnostic } from 'vscode-languageserver';
55
import { Logger } from './logger';
66
import { urlToPath } from './utils';
77

8+
export interface SvelteCheckOptions {
9+
compilerWarnings?: Record<string, 'ignore' | 'error'>;
10+
}
11+
812
/**
913
* Small wrapper around PluginHost's Diagnostic Capabilities
1014
* for svelte-check, without the overhead of the lsp.
@@ -16,12 +20,17 @@ export class SvelteCheck {
1620
private configManager = new LSConfigManager();
1721
private pluginHost = new PluginHost(this.docManager, this.configManager);
1822

19-
constructor(workspacePath: string) {
23+
constructor(workspacePath: string, options: SvelteCheckOptions = {}) {
2024
Logger.setLogErrorsOnly(true);
21-
this.initialize(workspacePath);
25+
this.initialize(workspacePath, options);
2226
}
2327

24-
private initialize(workspacePath: string) {
28+
private initialize(workspacePath: string, options: SvelteCheckOptions) {
29+
this.configManager.update({
30+
svelte: {
31+
compilerWarnings: options.compilerWarnings,
32+
},
33+
});
2534
this.pluginHost.register(new SveltePlugin(this.configManager, {}));
2635
this.pluginHost.register(new HTMLPlugin(this.docManager, this.configManager));
2736
this.pluginHost.register(new CSSPlugin(this.docManager, this.configManager));

packages/language-server/test/plugins/svelte/features/getDiagnostics.test.ts

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@ import {
77
TranspileErrorSource,
88
} from '../../../../src/plugins/svelte/SvelteDocument';
99
import { SvelteConfig } from '../../../../src/lib/documents/configLoader';
10+
import { CompilerWarningsSettings } from '../../../../src/ls-config';
1011

1112
describe('SveltePlugin#getDiagnostics', () => {
1213
async function expectDiagnosticsFor(
1314
getTranspiled: any,
1415
getCompiled: any,
1516
config: Partial<SvelteConfig>,
17+
settings: CompilerWarningsSettings = {},
1618
) {
1719
const document = new Document('', '<script></script>\n<style></style>');
1820
const svelteDoc: SvelteDocument = <any>{ getTranspiled, getCompiled, config };
19-
const result = await getDiagnostics(document, svelteDoc);
21+
const result = await getDiagnostics(document, svelteDoc, settings);
2022
return {
2123
toEqual: (expected: Diagnostic[]) => assert.deepStrictEqual(result, expected),
2224
};
@@ -259,4 +261,77 @@ describe('SveltePlugin#getDiagnostics', () => {
259261
},
260262
]);
261263
});
264+
265+
it('filter out warnings', async () => {
266+
(
267+
await expectDiagnosticsFor(
268+
() => ({
269+
getOriginalPosition: (pos: Position) => {
270+
pos.line - 1;
271+
return pos;
272+
},
273+
}),
274+
() =>
275+
Promise.resolve({
276+
stats: {
277+
warnings: [
278+
{
279+
start: { line: 1, column: 0 },
280+
end: { line: 1, column: 0 },
281+
message: 'warning',
282+
code: '123',
283+
},
284+
],
285+
},
286+
}),
287+
{},
288+
{ '123': 'ignore' },
289+
)
290+
).toEqual([]);
291+
});
292+
293+
it('treat warnings as error', async () => {
294+
(
295+
await expectDiagnosticsFor(
296+
() => ({
297+
getOriginalPosition: (pos: Position) => {
298+
pos.line - 1;
299+
return pos;
300+
},
301+
}),
302+
() =>
303+
Promise.resolve({
304+
stats: {
305+
warnings: [
306+
{
307+
start: { line: 1, column: 0 },
308+
end: { line: 1, column: 0 },
309+
message: 'warning',
310+
code: '123',
311+
},
312+
],
313+
},
314+
}),
315+
{},
316+
{ '123': 'error' },
317+
)
318+
).toEqual([
319+
{
320+
code: '123',
321+
message: 'warning',
322+
range: {
323+
start: {
324+
character: 0,
325+
line: 0,
326+
},
327+
end: {
328+
character: 0,
329+
line: 0,
330+
},
331+
},
332+
severity: DiagnosticSeverity.Error,
333+
source: 'svelte',
334+
},
335+
]);
336+
});
262337
});

packages/svelte-check/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Usage:
6060

6161
`--fail-on-warnings` Will also exit with error code when there are warnings
6262

63+
`--compiler-warnings <code1:error|ignore,code2:error|ignore>` A list of Svelte compiler warning codes. Each entry defines whether that warning should be ignored or treated as an error. Warnings are comma-separated, between warning code and error level is a colon; all inside quotes. Example: --compiler-warnings "css-unused-selector:ignore,unused-export-let:error"
64+
6365
### More docs, preprocessor setup and troubleshooting
6466

6567
[See here](/docs/README.md).

packages/svelte-check/src/index.ts

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as fs from 'fs';
66
import * as glob from 'glob';
77
import * as argv from 'minimist';
88
import * as path from 'path';
9-
import { SvelteCheck } from 'svelte-language-server';
9+
import { SvelteCheck, SvelteCheckOptions } from 'svelte-language-server';
1010
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-protocol';
1111
import { URI } from 'vscode-uri';
1212
import { HumanFriendlyWriter, MachineFriendlyWriter, Writer } from './writers';
@@ -122,6 +122,38 @@ class DiagnosticsWatcher {
122122
}
123123
}
124124

125+
function instantiateWriter(myArgs: argv.ParsedArgs): Writer {
126+
const outputFormat: OutputFormat = outputFormats.includes(myArgs['output'])
127+
? myArgs['output']
128+
: 'human-verbose';
129+
130+
if (outputFormat === 'human-verbose' || outputFormat === 'human') {
131+
return new HumanFriendlyWriter(process.stdout, outputFormat === 'human-verbose');
132+
} else {
133+
return new MachineFriendlyWriter(process.stdout);
134+
}
135+
}
136+
137+
function getOptions(myArgs: argv.ParsedArgs): SvelteCheckOptions {
138+
return {
139+
compilerWarnings: stringToObj(myArgs['compiler-warnings']),
140+
};
141+
142+
function stringToObj(str = '') {
143+
return str
144+
.split(',')
145+
.map((s) => s.trim())
146+
.filter((s) => !!s)
147+
.reduce((settings, setting) => {
148+
const [name, val] = setting.split(':');
149+
if (val === 'error' || val === 'ignore') {
150+
settings[name] = val;
151+
}
152+
return settings;
153+
}, <Record<string, 'error' | 'ignore'>>{});
154+
}
155+
}
156+
125157
(async () => {
126158
const myArgs = argv(process.argv.slice(1));
127159
let workspaceUri;
@@ -136,18 +168,9 @@ class DiagnosticsWatcher {
136168
workspaceUri = URI.file(process.cwd());
137169
}
138170

139-
const outputFormat: OutputFormat = outputFormats.includes(myArgs['output'])
140-
? myArgs['output']
141-
: 'human-verbose';
142-
let writer: Writer;
143-
144-
if (outputFormat === 'human-verbose' || outputFormat === 'human') {
145-
writer = new HumanFriendlyWriter(process.stdout, outputFormat === 'human-verbose');
146-
} else {
147-
writer = new MachineFriendlyWriter(process.stdout);
148-
}
171+
const writer = instantiateWriter(myArgs);
149172

150-
const svelteCheck = new SvelteCheck(workspaceUri.fsPath);
173+
const svelteCheck = new SvelteCheck(workspaceUri.fsPath, getOptions(myArgs));
151174
const filePathsToIgnore = myArgs['ignore']?.split(',') || [];
152175

153176
if (myArgs['watch']) {

packages/svelte-vscode/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ Enable the Svelte plugin. _Default_: `true`
148148

149149
Enable diagnostic messages for Svelte. _Default_: `true`
150150

151+
##### `svelte.plugin.svelte.compilerWarnings`
152+
153+
Svelte compiler warning codes to ignore or to treat as errors. Example: { 'css-unused-selector': 'ignore', 'unused-export-let': 'error'}
154+
151155
##### `svelte.plugin.svelte.format.enable`
152156

153157
Enable formatting for Svelte (includes css & js). _Default_: `true`

packages/svelte-vscode/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@
188188
"title": "Svelte: Diagnostics",
189189
"description": "Enable diagnostic messages for Svelte"
190190
},
191+
"svelte.plugin.svelte.compilerWarnings": {
192+
"type": "object",
193+
"additionalProperties": {
194+
"type": "string",
195+
"enum": [
196+
"ignore",
197+
"error"
198+
]
199+
},
200+
"default": {},
201+
"title": "Svelte: Compiler Warnings Settings",
202+
"description": "Svelte compiler warning codes to ignore or to treat as errors. Example: { 'css-unused-selector': 'ignore', 'unused-export-let': 'error'}"
203+
},
191204
"svelte.plugin.svelte.format.enable": {
192205
"type": "boolean",
193206
"default": true,

0 commit comments

Comments
 (0)