Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/language-server/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './server';
export { offsetAt } from './lib/documents';
export { SvelteCheck } from './svelte-check';
export { SvelteCheck, SvelteCheckOptions } from './svelte-check';
34 changes: 32 additions & 2 deletions packages/language-server/src/ls-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const defaultLSConfig: LSConfig = {
},
svelte: {
enable: true,
compilerWarnings: {},
diagnostics: { enable: true },
format: { enable: true },
completions: { enable: true },
Expand Down Expand Up @@ -114,8 +115,11 @@ export interface LSHTMLConfig {
};
}

export type CompilerWarningsSettings = Record<string, 'ignore' | 'error'>;

export interface LSSvelteConfig {
enable: boolean;
compilerWarnings: CompilerWarningsSettings;
diagnostics: {
enable: boolean;
};
Expand All @@ -133,25 +137,51 @@ export interface LSSvelteConfig {
};
}

type DeepPartial<T> = T extends CompilerWarningsSettings
? T
: {
[P in keyof T]?: DeepPartial<T[P]>;
};

export class LSConfigManager {
private config: LSConfig = defaultLSConfig;

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

/**
* Whether or not specified config is enabled
* @param key a string which is a path. Example: 'svelte.diagnostics.enable'.
*/
enabled(key: string): boolean {
return !!get(this.config, key);
return !!this.get(key);
}

/**
* Get specific config
* @param key a string which is a path. Example: 'svelte.diagnostics.enable'.
*/
get<T>(key: string): T {
return get(this.config, key);
}

/**
* Get the whole config
*/
getConfig(): Readonly<LSConfig> {
return this.config;
}
}

Expand Down
11 changes: 5 additions & 6 deletions packages/language-server/src/plugins/svelte/SveltePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { cosmiconfig } from 'cosmiconfig';
import {
CodeAction,
CodeActionContext,
Expand Down Expand Up @@ -34,10 +33,6 @@ export class SveltePlugin
HoverProvider,
CodeActionsProvider {
private docManager = new Map<Document, SvelteDocument>();
private cosmiConfigExplorer = cosmiconfig('svelte', {
packageProp: 'svelte-ls',
cache: true,
});

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

Expand All @@ -46,7 +41,11 @@ export class SveltePlugin
return [];
}

return getDiagnostics(document, await this.getSvelteDoc(document));
return getDiagnostics(
document,
await this.getSvelteDoc(document),
this.configManager.getConfig().svelte.compilerWarnings,
);
}

async getCompiledResult(document: Document): Promise<SvelteCompileResult | null> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Diagnostic, DiagnosticSeverity, Position, Range } from 'vscode-language
import { Document, isInTag, mapDiagnosticToOriginal } from '../../../lib/documents';
import { Logger } from '../../../logger';
import { SvelteDocument, TranspileErrorSource } from '../SvelteDocument';
import { CompilerWarningsSettings } from '../../../ls-config';

/**
* Returns diagnostics from the svelte compiler.
Expand All @@ -11,13 +12,14 @@ import { SvelteDocument, TranspileErrorSource } from '../SvelteDocument';
export async function getDiagnostics(
document: Document,
svelteDoc: SvelteDocument,
settings: CompilerWarningsSettings,
): Promise<Diagnostic[]> {
if (svelteDoc.config.loadConfigError) {
return getConfigLoadErrorDiagnostics(svelteDoc.config.loadConfigError);
}

try {
return await tryGetDiagnostics(document, svelteDoc);
return await tryGetDiagnostics(document, svelteDoc, settings);
} catch (error) {
return getPreprocessErrorDiagnostics(document, error);
}
Expand All @@ -29,19 +31,24 @@ export async function getDiagnostics(
async function tryGetDiagnostics(
document: Document,
svelteDoc: SvelteDocument,
settings: CompilerWarningsSettings,
): Promise<Diagnostic[]> {
const transpiled = await svelteDoc.getTranspiled();

try {
const res = await svelteDoc.getCompiled();
return (((res.stats as any).warnings || res.warnings || []) as Warning[])
.filter((warning) => settings[warning.code] !== 'ignore')
.map((warning) => {
const start = warning.start || { line: 1, column: 0 };
const end = warning.end || start;
return {
range: Range.create(start.line - 1, start.column, end.line - 1, end.column),
message: warning.message,
severity: DiagnosticSeverity.Warning,
severity:
settings[warning.code] === 'error'
? DiagnosticSeverity.Error
: DiagnosticSeverity.Warning,
source: 'svelte',
code: warning.code,
};
Expand Down
15 changes: 12 additions & 3 deletions packages/language-server/src/svelte-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Diagnostic } from 'vscode-languageserver';
import { Logger } from './logger';
import { urlToPath } from './utils';

export interface SvelteCheckOptions {
compilerWarnings?: Record<string, 'ignore' | 'error'>;
}

/**
* Small wrapper around PluginHost's Diagnostic Capabilities
* for svelte-check, without the overhead of the lsp.
Expand All @@ -16,12 +20,17 @@ export class SvelteCheck {
private configManager = new LSConfigManager();
private pluginHost = new PluginHost(this.docManager, this.configManager);

constructor(workspacePath: string) {
constructor(workspacePath: string, options: SvelteCheckOptions = {}) {
Logger.setLogErrorsOnly(true);
this.initialize(workspacePath);
this.initialize(workspacePath, options);
}

private initialize(workspacePath: string) {
private initialize(workspacePath: string, options: SvelteCheckOptions) {
this.configManager.update({
svelte: {
compilerWarnings: options.compilerWarnings,
},
});
this.pluginHost.register(new SveltePlugin(this.configManager, {}));
this.pluginHost.register(new HTMLPlugin(this.docManager, this.configManager));
this.pluginHost.register(new CSSPlugin(this.docManager, this.configManager));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import {
TranspileErrorSource,
} from '../../../../src/plugins/svelte/SvelteDocument';
import { SvelteConfig } from '../../../../src/lib/documents/configLoader';
import { CompilerWarningsSettings } from '../../../../src/ls-config';

describe('SveltePlugin#getDiagnostics', () => {
async function expectDiagnosticsFor(
getTranspiled: any,
getCompiled: any,
config: Partial<SvelteConfig>,
settings: CompilerWarningsSettings = {},
) {
const document = new Document('', '<script></script>\n<style></style>');
const svelteDoc: SvelteDocument = <any>{ getTranspiled, getCompiled, config };
const result = await getDiagnostics(document, svelteDoc);
const result = await getDiagnostics(document, svelteDoc, settings);
return {
toEqual: (expected: Diagnostic[]) => assert.deepStrictEqual(result, expected),
};
Expand Down Expand Up @@ -259,4 +261,77 @@ describe('SveltePlugin#getDiagnostics', () => {
},
]);
});

it('filter out warnings', async () => {
(
await expectDiagnosticsFor(
() => ({
getOriginalPosition: (pos: Position) => {
pos.line - 1;
return pos;
},
}),
() =>
Promise.resolve({
stats: {
warnings: [
{
start: { line: 1, column: 0 },
end: { line: 1, column: 0 },
message: 'warning',
code: '123',
},
],
},
}),
{},
{ '123': 'ignore' },
)
).toEqual([]);
});

it('treat warnings as error', async () => {
(
await expectDiagnosticsFor(
() => ({
getOriginalPosition: (pos: Position) => {
pos.line - 1;
return pos;
},
}),
() =>
Promise.resolve({
stats: {
warnings: [
{
start: { line: 1, column: 0 },
end: { line: 1, column: 0 },
message: 'warning',
code: '123',
},
],
},
}),
{},
{ '123': 'error' },
)
).toEqual([
{
code: '123',
message: 'warning',
range: {
start: {
character: 0,
line: 0,
},
end: {
character: 0,
line: 0,
},
},
severity: DiagnosticSeverity.Error,
source: 'svelte',
},
]);
});
});
2 changes: 2 additions & 0 deletions packages/svelte-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Usage:

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

`--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"

### More docs, preprocessor setup and troubleshooting

[See here](/docs/README.md).
Expand Down
47 changes: 35 additions & 12 deletions packages/svelte-check/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as fs from 'fs';
import * as glob from 'glob';
import * as argv from 'minimist';
import * as path from 'path';
import { SvelteCheck } from 'svelte-language-server';
import { SvelteCheck, SvelteCheckOptions } from 'svelte-language-server';
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-protocol';
import { URI } from 'vscode-uri';
import { HumanFriendlyWriter, MachineFriendlyWriter, Writer } from './writers';
Expand Down Expand Up @@ -122,6 +122,38 @@ class DiagnosticsWatcher {
}
}

function instantiateWriter(myArgs: argv.ParsedArgs): Writer {
const outputFormat: OutputFormat = outputFormats.includes(myArgs['output'])
? myArgs['output']
: 'human-verbose';

if (outputFormat === 'human-verbose' || outputFormat === 'human') {
return new HumanFriendlyWriter(process.stdout, outputFormat === 'human-verbose');
} else {
return new MachineFriendlyWriter(process.stdout);
}
}

function getOptions(myArgs: argv.ParsedArgs): SvelteCheckOptions {
return {
compilerWarnings: stringToObj(myArgs['compiler-warnings']),
};

function stringToObj(str = '') {
return str
.split(',')
.map((s) => s.trim())
.filter((s) => !!s)
.reduce((settings, setting) => {
const [name, val] = setting.split(':');
if (val === 'error' || val === 'ignore') {
settings[name] = val;
}
return settings;
}, <Record<string, 'error' | 'ignore'>>{});
}
}

(async () => {
const myArgs = argv(process.argv.slice(1));
let workspaceUri;
Expand All @@ -136,18 +168,9 @@ class DiagnosticsWatcher {
workspaceUri = URI.file(process.cwd());
}

const outputFormat: OutputFormat = outputFormats.includes(myArgs['output'])
? myArgs['output']
: 'human-verbose';
let writer: Writer;

if (outputFormat === 'human-verbose' || outputFormat === 'human') {
writer = new HumanFriendlyWriter(process.stdout, outputFormat === 'human-verbose');
} else {
writer = new MachineFriendlyWriter(process.stdout);
}
const writer = instantiateWriter(myArgs);

const svelteCheck = new SvelteCheck(workspaceUri.fsPath);
const svelteCheck = new SvelteCheck(workspaceUri.fsPath, getOptions(myArgs));
const filePathsToIgnore = myArgs['ignore']?.split(',') || [];

if (myArgs['watch']) {
Expand Down
4 changes: 4 additions & 0 deletions packages/svelte-vscode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ Enable the Svelte plugin. _Default_: `true`

Enable diagnostic messages for Svelte. _Default_: `true`

##### `svelte.plugin.svelte.compilerWarnings`

Svelte compiler warning codes to ignore or to treat as errors. Example: { 'css-unused-selector': 'ignore', 'unused-export-let': 'error'}

##### `svelte.plugin.svelte.format.enable`

Enable formatting for Svelte (includes css & js). _Default_: `true`
Expand Down
13 changes: 13 additions & 0 deletions packages/svelte-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@
"title": "Svelte: Diagnostics",
"description": "Enable diagnostic messages for Svelte"
},
"svelte.plugin.svelte.compilerWarnings": {
"type": "object",
"additionalProperties": {
"type": "string",
"enum": [
"ignore",
"error"
]
},
"default": {},
"title": "Svelte: Compiler Warnings Settings",
"description": "Svelte compiler warning codes to ignore or to treat as errors. Example: { 'css-unused-selector': 'ignore', 'unused-export-let': 'error'}"
},
"svelte.plugin.svelte.format.enable": {
"type": "boolean",
"default": true,
Expand Down