Skip to content

Commit

Permalink
(feat) svelte-check: watch mode, ignore/fail-on-warnings options (#373)
Browse files Browse the repository at this point in the history
  • Loading branch information
dummdidumm committed Jul 28, 2020
1 parent e84e0e2 commit c0a21b4
Show file tree
Hide file tree
Showing 10 changed files with 218 additions and 84 deletions.
12 changes: 7 additions & 5 deletions packages/language-server/src/lib/documents/DocumentManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ export class DocumentManager {
public locked = new Set<string>();
public deleteCandidates = new Set<string>();

constructor(private createDocument: (textDocument: TextDocumentItem) => Document) {}
constructor(
private createDocument: (textDocument: Pick<TextDocumentItem, 'text' | 'uri'>) => Document,
) {}

openDocument(textDocument: TextDocumentItem): Document {
openDocument(textDocument: Pick<TextDocumentItem, 'text' | 'uri'>): Document {
let document: Document;
if (this.documents.has(textDocument.uri)) {
document = this.documents.get(textDocument.uri)!;
Expand All @@ -45,8 +47,9 @@ export class DocumentManager {
}

getAllOpenedByClient() {
return Array.from(this.documents.entries())
.filter((doc) => this.openedInClient.has(doc[0]));
return Array.from(this.documents.entries()).filter((doc) =>
this.openedInClient.has(doc[0]),
);
}

releaseDocument(uri: string): void {
Expand All @@ -58,7 +61,6 @@ export class DocumentManager {
}
}


closeDocument(uri: string) {
const document = this.documents.get(uri);
if (!document) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@ export class LSAndTSDocResolver {
private createDocument = (fileName: string, content: string) => {
const uri = pathToUrl(fileName);
const document = this.docManager.openDocument({
languageId: 'svelte',
text: content,
uri,
version: 0,
});
this.docManager.lockDocument(uri);
return document;
Expand Down
44 changes: 36 additions & 8 deletions packages/language-server/src/svelte-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { LSConfigManager } from './ls-config';
import { CSSPlugin, HTMLPlugin, PluginHost, SveltePlugin, TypeScriptPlugin } from './plugins';
import { Diagnostic } from 'vscode-languageserver';
import { Logger } from './logger';
import { urlToPath } from './utils';

/**
* Small wrapper around PluginHost's Diagnostic Capabilities
Expand Down Expand Up @@ -30,17 +31,44 @@ export class SvelteCheck {
}

/**
* Gets diagnostics for a svelte file.
* Creates/updates given document
*
* @param params Text and Uri of a svelte file
* @param doc Text and Uri of the document
*/
async getDiagnostics(params: { text: string; uri: string }): Promise<Diagnostic[]> {
upsertDocument(doc: { text: string; uri: string }) {
this.docManager.openDocument({
languageId: 'svelte',
text: params.text,
uri: params.uri,
version: 1,
text: doc.text,
uri: doc.uri,
});
return await this.pluginHost.getDiagnostics({ uri: params.uri });
this.docManager.markAsOpenedInClient(doc.uri);
}

/**
* Removes/closes document
*
* @param uri Uri of the document
*/
removeDocument(uri: string) {
this.docManager.closeDocument(uri);
this.docManager.releaseDocument(uri);
}

/**
* Gets the diagnostics for all currently open files.
*/
async getDiagnostics(): Promise<
{ filePath: string; text: string; diagnostics: Diagnostic[] }[]
> {
return await Promise.all(
this.docManager.getAllOpenedByClient().map(async (doc) => {
const uri = doc[1].uri;
const diagnostics = await this.pluginHost.getDiagnostics({ uri });
return {
filePath: urlToPath(uri) || '',
text: this.docManager.documents.get(uri)?.getText() || '',
diagnostics,
};
}),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Document Manager', () => {
text: 'Hello, world!',
};

const createTextDocument = (textDocument: TextDocumentItem) =>
const createTextDocument = (textDocument: Pick<TextDocumentItem, 'uri' | 'text'>) =>
new Document(textDocument.uri, textDocument.text);

it('opens documents', () => {
Expand Down
7 changes: 3 additions & 4 deletions packages/language-server/test/plugins/PluginHost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ describe('PluginHost', () => {
};

function setup<T>(pluginProviderStubs: T) {
const createTextDocument = (textDocument: TextDocumentItem) =>
new Document(textDocument.uri, textDocument.text);

const docManager = new DocumentManager(createTextDocument);
const docManager = new DocumentManager(
(textDocument) => new Document(textDocument.uri, textDocument.text),
);

const pluginHost = new PluginHost(docManager, <any>{});
const plugin = {
Expand Down
6 changes: 6 additions & 0 deletions packages/svelte-check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ Usage:

`--output <human|human-verbose|machine>`

`--watch` Will not exit after one pass but keep watching files for changes and rerun diagnostics.

`--ignore <files/folders to ignore, relative to workspace root, comma-separated. Example: --ignore dist,build>`

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

### More docs, preprocessor setup and troubleshooting

[See here](/docs/README.md).
Expand Down
1 change: 1 addition & 0 deletions packages/svelte-check/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"homepage": "https://github.com/sveltejs/language-tools#readme",
"dependencies": {
"chalk": "^4.0.0",
"chokidar": "^3.4.1",
"glob": "^7.1.6",
"minimist": "^1.2.5",
"svelte-language-server": "*",
Expand Down
137 changes: 98 additions & 39 deletions packages/svelte-check/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SvelteCheck } from 'svelte-language-server';
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver-protocol';
import { URI } from 'vscode-uri';
import { HumanFriendlyWriter, MachineFriendlyWriter, Writer } from './writers';
import { watch } from 'chokidar';

const outputFormats = ['human', 'human-verbose', 'machine'] as const;
type OutputFormat = typeof outputFormats[number];
Expand All @@ -20,58 +21,105 @@ type Result = {
warningCount: number;
};

async function getDiagnostics(workspaceUri: URI, writer: Writer): Promise<Result | null> {
writer.start(workspaceUri.fsPath);

const svelteCheck = new SvelteCheck(workspaceUri.fsPath);

function openAllDocuments(
workspaceUri: URI,
filePathsToIgnore: string[],
svelteCheck: SvelteCheck,
) {
const files = glob.sync('**/*.svelte', {
cwd: workspaceUri.fsPath,
ignore: ['node_modules/**'],
ignore: ['node_modules/**'].concat(filePathsToIgnore.map((ignore) => `${ignore}/**`)),
});

const absFilePaths = files.map((f) => path.resolve(workspaceUri.fsPath, f));

const result = {
fileCount: absFilePaths.length,
errorCount: 0,
warningCount: 0,
};

for (const absFilePath of absFilePaths) {
const text = fs.readFileSync(absFilePath, 'utf-8');
svelteCheck.upsertDocument({
uri: URI.file(absFilePath).toString(),
text,
});
}
}

let res: Diagnostic[] = [];
async function getDiagnostics(
workspaceUri: URI,
writer: Writer,
svelteCheck: SvelteCheck,
): Promise<Result | null> {
writer.start(workspaceUri.fsPath);

try {
res = await svelteCheck.getDiagnostics({
uri: URI.file(absFilePath).toString(),
text,
try {
const diagnostics = await svelteCheck.getDiagnostics();

const result: Result = {
fileCount: diagnostics.length,
errorCount: 0,
warningCount: 0,
};

for (const diagnostic of diagnostics) {
writer.file(
diagnostic.diagnostics,
workspaceUri.fsPath,
path.relative(workspaceUri.fsPath, diagnostic.filePath),
diagnostic.text,
);

diagnostic.diagnostics.forEach((d: Diagnostic) => {
if (d.severity === DiagnosticSeverity.Error) {
result.errorCount += 1;
} else if (d.severity === DiagnosticSeverity.Warning) {
result.warningCount += 1;
}
});
} catch (err) {
writer.failure(err);
return null;
}

writer.file(
res,
workspaceUri.fsPath,
path.relative(workspaceUri.fsPath, absFilePath),
text,
);
writer.completion(result.fileCount, result.errorCount, result.warningCount);
return result;
} catch (err) {
writer.failure(err);
return null;
}
}

res.forEach((d: Diagnostic) => {
if (d.severity === DiagnosticSeverity.Error) {
result.errorCount += 1;
} else if (d.severity === DiagnosticSeverity.Warning) {
result.warningCount += 1;
}
});
class DiagnosticsWatcher {
private updateDiagnostics: any;

constructor(
private workspaceUri: URI,
private svelteCheck: SvelteCheck,
private writer: Writer,
filePathsToIgnore: string[],
) {
watch(`${workspaceUri.fsPath}/**/*.svelte`, {
ignored: ['node_modules']
.concat(filePathsToIgnore)
.map((ignore) => path.join(workspaceUri.fsPath, ignore)),
})
.on('add', (path) => this.updateDocument(path))
.on('unlink', (path) => this.removeDocument(path))
.on('change', (path) => this.updateDocument(path));
}

writer.completion(result.fileCount, result.errorCount, result.warningCount);
private updateDocument(path: string) {
const text = fs.readFileSync(path, 'utf-8');
this.svelteCheck.upsertDocument({ text, uri: URI.file(path).toString() });
this.scheduleDiagnostics();
}

return result;
private removeDocument(path: string) {
this.svelteCheck.removeDocument(URI.file(path).toString());
this.scheduleDiagnostics();
}

private scheduleDiagnostics() {
clearTimeout(this.updateDiagnostics);
this.updateDiagnostics = setTimeout(
() => getDiagnostics(this.workspaceUri, this.writer, this.svelteCheck),
1000,
);
}
}

(async () => {
Expand Down Expand Up @@ -99,12 +147,23 @@ async function getDiagnostics(workspaceUri: URI, writer: Writer): Promise<Result
writer = new MachineFriendlyWriter(process.stdout);
}

const result = await getDiagnostics(workspaceUri, writer);
const svelteCheck = new SvelteCheck(workspaceUri.fsPath);
const filePathsToIgnore = myArgs['ignore'].split(' ') || [];

if (result && (result as Result).errorCount === 0) {
process.exit(0);
if (myArgs['watch']) {
new DiagnosticsWatcher(workspaceUri, svelteCheck, writer, filePathsToIgnore);
} else {
process.exit(1);
openAllDocuments(workspaceUri, filePathsToIgnore, svelteCheck);
const result = await getDiagnostics(workspaceUri, writer, svelteCheck);
if (
result &&
result.errorCount === 0 &&
(!myArgs['fail-on-warnings'] || result.warningCount === 0)
) {
process.exit(0);
} else {
process.exit(1);
}
}
})().catch((_err) => {
console.error(_err);
Expand Down

0 comments on commit c0a21b4

Please sign in to comment.