Challenge 4: Mini plugin
- TypeScript course
- Expert
- Lesson 35/35
- 6 min
Solve a task testing the full expert level.
Mini plugin
This final challenge tests your full expert level. Write a lint plugin skeleton, wrap getSemanticDiagnostics, and test it with a mock language service. Then add a custom diagnostic producer that flags any types. You will use ts.Diagnostic, ts.forEachChild and ts.isVariableDeclaration together.
Practice Plugin skeleton
Write a plugin function that takes ts.server.PluginCreateInfo and returns ts.LanguageService. Get ls from info.languageService, wrap getSemanticDiagnostics and return ls's diagnostics as-is. A mock language service returns 2 diagnostics. Call the plugin and print the diagnostic count.
Starter code
import * as ts from "typescript";
// Bir plugin fonksiyonu yaz:
// - PluginCreateInfo alır, LanguageService döndürür
// - info'daki dil servisini al
// - Diagnostic metodunu sar ve ls'nin diagnostics'ini döndür
// Mock dil servisi 2 diagnostic döndürsün
// Plugin'i çağır ve diagnostic sayısını yazdır
// kodunu buraya yazSample solution
import ts from "typescript";
function createPlugin(info: ts.server.PluginCreateInfo): ts.LanguageService {
const ls = info.languageService;
return {
...ls,
getSemanticDiagnostics: function(fileName: string) {
return ls.getSemanticDiagnostics(fileName);
},
};
}
const fakeLs = {
getSemanticDiagnostics: function(_fileName: string): ts.Diagnostic[] {
return [
{ category: ts.DiagnosticCategory.Error, code: 1, messageText: "a", file: undefined, start: 0, length: 1 } as ts.Diagnostic,
{ category: ts.DiagnosticCategory.Warning, code: 2, messageText: "b", file: undefined, start: 0, length: 1 } as ts.Diagnostic,
];
},
} as unknown as ts.LanguageService;
const info = { languageService: fakeLs, config: {} } as unknown as ts.server.PluginCreateInfo;
const plugin = createPlugin(info);
const diags = plugin.getSemanticDiagnostics("app.ts");
console.log(diags.length);Expected output
2
Practice Flag any types
Write a plugin that wraps getSemanticDiagnostics. Get the SourceFile via ls.getProgram().getSourceFile(fileName). Walk the AST: for each VariableDeclaration whose type annotation is any, produce a ts.Diagnostic (category: Warning, messageText: "Avoid any"). Merge prior and extras. Test: source let a: any = 1; let b: number = 2;; print the total diagnostic count.
Starter code
import * as ts from "typescript";
// Bir plugin yaz: Diagnostic metodunu sar
// - SourceFile'ı al
// - AST'yı gez: any türü olan VariableDeclaration bul
// - Her biri için bir Diagnostic üret (Warning kategorisi)
// - prior ve extras'ı birleştir
// Test: kaynak "let a: any = 1; let b: number = 2;"
// Toplam diagnostic sayısını yazdır
// kodunu buraya yazSample solution
import ts from "typescript";
function createPlugin(info: ts.server.PluginCreateInfo): ts.LanguageService {
const ls = info.languageService;
return {
...ls,
getSemanticDiagnostics: function(fileName: string) {
const prior = ls.getSemanticDiagnostics(fileName);
const sf = ls.getProgram().getSourceFile(fileName);
if (!sf) return prior;
const extras: ts.Diagnostic[] = [];
function visit(node: ts.Node) {
if (ts.isVariableDeclaration(node) && node.type && node.type.kind === ts.SyntaxKind.AnyKeyword) {
extras.push({
file: sf,
start: node.getStart(sf),
length: node.getWidth(sf),
category: ts.DiagnosticCategory.Warning,
code: 9001,
messageText: "Avoid any",
});
}
ts.forEachChild(node, visit);
}
visit(sf);
return [...prior, ...extras];
},
};
}
const sf = ts.createSourceFile("app.ts", "let a: any = 1; let b: number = 2;", ts.ScriptTarget.Latest, true);
const fakeLs = {
getSemanticDiagnostics: function() { return [] as ts.Diagnostic[]; },
getProgram: function() { return { getSourceFile: function(n: string) { return n === "app.ts" ? sf : undefined; } } as unknown as ts.Program; },
} as unknown as ts.LanguageService;
const info = { languageService: fakeLs, config: {} } as unknown as ts.server.PluginCreateInfo;
const plugin = createPlugin(info);
const diags = plugin.getSemanticDiagnostics("app.ts");
console.log(diags.length);Expected output
1
Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 Which method does a plugin wrap to produce diagnostics?
- getCompilerDiagnostics
- getSyntacticDiagnostics
- getSemanticDiagnostics Correct answer
- getSuggestionDiagnostics
Explanation: A lint plugin wraps getSemanticDiagnostics and adds custom diagnostics. getSyntacticDiagnostics, getSuggestionDiagnostics and getCompilerDiagnostics are not used in the plugin pattern.
2 Get the message text
console.log((diags[0] as ts.Diagnostic).____);
console.log((diags[0] as ts.Diagnostic).messageText);
Explanation: ts.Diagnostic.messageText carries the message text; message, text and description are not properties on Diagnostic.
3 Plugin parts
| Term | Meaning |
|---|---|
PluginCreateInfo |
Plugin input |
info.languageService |
Existing service |
ts.Diagnostic |
Error/warning object |
ts.forEachChild |
Walks the AST |